2009-04-13 6 views
4

J'essaie de réutiliser certaines colonnes que je calcule dynamiquement dans Oracle SQL, quelque chose commeComment réutiliser des colonnes dynamiques dans une instruction SQL Oracle?

SELECT 
    A*2 AS P, 
    P+5 AS Q 
FROM tablename 

Où tablename 'a une colonne appelée « A », mais aucun autre colums. Cela me donne un

ORA-00904: "P": invalid identifier 

Je sais comment contourner ce problème en utilisant un sous-requête comme

SELECT P, P+5 AS Q 
FROM (SELECT A*2 AS P FROM tablename) 

mais je pense que cela est un peu laid. En outre, je veux rendre la requête un peu plus complexe, par ex. réutiliser 'Q' aussi, et je ne veux pas créer encore une autre sous-requête.

Mise à jour: La raison pour laquelle je veux stocker le calcul de 'P' est que je veux le rendre plus complexe, et réutiliser plusieurs fois 'P'. Donc je ne veux pas dire explicitement 'A * 2 + 5 AS Q', parce que cela deviendrait vite lourd, car 'P' devient plus complexe.

Il doit y avoir un bon moyen de le faire, des idées?

Mise à jour: Je dois souligner que je ne suis pas un DB-admin :(


Mise à jour:.. Un exemple du monde réel, avec une requête plus concrète ce que je voudrais faire est:

SELECT 
    SL/SQRT(AB) AS ALPHA, 
    5*LOG(10,ALPHA) AS B, 
    2.5*LOG(10,1-EXP(-5/ALPHA)*(5/ALPHA+1)) AS D 
    BS -2.74 + B + D AS BSA 
FROM tablename 

pour l'instant, je l'ai écrit sur, ce qui fonctionne, mais est laid:

SELECT 
    SL/SQRT(AB) AS ALPHA, 
    5*LOG(10,SL/SQRT(AB)) AS B, 
    2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*(5/(SL/SQRT(AB))+1)) AS D 
    BS -2.74 + 5*LOG(10,SL/SQRT(AB)) + 2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*((5/(SL/SQRT(AB)))+1)) AS BSA 
FROM tablename 

que je pouvais faire tout cela après avoir reçu les données, mais je pensais, nous allons voir comment beaucoup je peux laisser la base de données faire. En outre, je voudrais également choisir sur 'BSA' (que je peux faire maintenant avec cette requête comme subquery/with-clause).


Mise à jour: OK, je pense que pour l'instant j'ai fini avec la solution de Cade Roux et Dave Costa. Bien que la solution de Pax 'et Jens Schauder soit meilleure, mais je ne peux pas les utiliser puisque je ne suis pas un administrateur de base de données. Maintenant, je ne sais pas qui marquer comme la meilleure réponse :). dans le cas où quelqu'un est intéressé

WITH 
    A1 AS ( 
    SELECT A0.*, 
    SL/SQRT(AB) AS ALPHA 
    FROM tablename A0 
), 
    A2 AS (
    SELECT A1.*, 
    5*LOG(10,ALPHA) AS B, 
    2.5*LOG(10,1-EXP(-5/ALPHA)*((5/ALPHA)+1)) AS D 
    FROM A1 
) 
SELECT 
    ALPHA, B, D, BS, 
    BS -2.74 + B + D AS BSA 
FROM A2 

BTW,, SB est la 'brillance de surface' de galaxies, pour lesquelles B et D sont des termes de correction.

Répondre

2

Nous avons ce même problème dans SQL Server (c'est un problème ANSI). Je crois qu'il est destiné à éviter les effets de confusion aliasing:

SELECT A * 2 AS A 
    ,A * 3 AS B -- This is the original A, not the new A 
FROM whatever 

Nous travaillons par empilement des expressions de table commune:

WITH A1 AS (
    SELECT A * 2 AS A 
    FROM whatever 
) 
,A2 AS (
    SELECT A1.* 
     ,A * 3 AS B 
    FROM A1 
) 
,A3 AS (
    SELECT A2.* 
     ,A + B AS X 
    FROM A2 
) 
SELECT * 
FROM A3 

Ceci est la version la plus lisible et maintable et followable. Pour les UPDATEs, il existe une solution de contournement SQL Server obsolète utilisant la notation nom_colonne =, dans laquelle vous pouvez référencer une colonne qui a été mise à jour précédemment dans la liste. Mais cela ne peut pas être utilisé dans les sélections.

J'espère qu'une certaine capacité à empiler des expressions (sans utiliser une UDF scalaire) est ajoutée à SQL ANSI à un moment donné dans le futur.

+0

haha, ça a quand même causé des effets secondaires! parce que j'ai d'abord utilisé 'T' et 'S' pour mes nouvelles colonnes, qui existaient! donc il n'y avait pas d'erreur, seulement des valeurs totalement incorrectes. (+ De 200 colonnes, tous les 2-5 caractères ...) – BlackShift

+0

Les résultats sont supposés être invariants dans le changement d'ordre de colonne. Heureusement, ANSI offrira un certain soulagement sous la forme d'une sorte de directive d'empilage en ligne. –

0

Je ne suis pas sûr que vous pouvez le faire (je ne l'ai jamais vu faire) mais vous pouvez travailler autour d'elle avec:

SELECT 
    A*2 AS P, 
    A*2+5 AS Q 
FROM tablename 

C'est certainement mieux que l'introduction d'un sous-requête. La seule autre façon que je suggère est de créer une vue en vous donnant les colonnes de type P/Q (en utilisant les formules ci-dessus) qui simplifierait au moins le texte de la requête. Alors vous pourriez simplement:

SELECT P, Q FROM viewintotablename 
+0

Oui cela fonctionne, j'ai fait la requête dans l'exemple simple, en réalité j'utilise plusieurs fois la valeur P pour calculer d'autres colonnes, et P est un peu plus compliqué. Je vais mettre à jour la question. – BlackShift

+0

Je ne suis pas entièrement sûr pourquoi vous êtes si préoccupé par la requête. Ils ont tendance à être écrits une fois puis laissés seuls, donc cela n'a pas d'importance à quel point ils sont «moche» (en supposant que les vraiment moche sont correctement documentés :-). – paxdiablo

+0

La seule chose que les DBA aient à se préoccuper est la vitesse brute, que je ne pense pas que vous seriez capable de réparer facilement sans dénormaliser (c'est-à-dire en introduisant des colonnes générées à la DB2). – paxdiablo

0

Vous ne pouvez pas.

Si vous ne voulez pas la sous-requête à réévaluées, ajoutez un soupçon NO_MERGE pour la sous-requête:

Cette sous-requête sera dans la boucle réévaluées imbriquée (MERGE indice est utilisé):

SELECT /*+ LEADING(g) USE_NL(g, r) MERGE(g) */ 
     * 
FROM (
     SELECT 1 
     FROM dual 
     UNION ALL 
     SELECT 2 
     FROM dual 
     ) r, 
     (
     SELECT SYS_GUID() AS guid 
     FROM dual d 
     ) g 

--- 
33CA48C1AB6B4403808FB0219302CE43 
711BB04F9AFC406ABAEF8A8F4CFA1266 

Cette sous-requête ne sera pas dans la boucle réévaluées imbriquée (NO_MERGE indice est utilisé):

SELECT /*+ LEADING(g) USE_NL(g, r) NO_MERGE(g) */ 
     * 
FROM (
     SELECT 1 
     FROM dual 
     UNION ALL 
     SELECT 2 
     FROM dual 
     ) r, 
     (
     SELECT SYS_GUID() AS guid 
     FROM dual d 
     ) g 

------ 
7715C69698A243C0B379E68ABB55C088 
7715C69698A243C0B379E68ABB55C088 

dans votre cas, jus t écrire:

SELECT BS - 2.74 + d 
FROM (
     SELECT t2.*, 2.5 * LOG(10, 1 - EXP(-5/b)) * ((5/A) + 1) AS d 
     FROM (
       SELECT t1.*, 5 * LOG(10, alpha) AS b 
       FROM (
         SELECT /*+ NO_MERGE */ t.*, 
           SL/SQRT(AB) AS alpha 
         FROM tablename t 
         ) t1 
       ) t2 
     ) t3 

, qui est plus efficace (EXP et LOG sont coûteux) et est beaucoup plus facile à déboguer.

+0

Je ne sais pas comment cela s'applique, mais je vais le garder à l'esprit. (Qu'il réévalue ou non est une question de vitesse.) – BlackShift

0

Il n'y a pas de moyen direct de faire cela en SQL.

Mais vous pouvez définir une fonction en utilisant PL/SQL. Ainsi, votre sélection ressemblerait à ceci

select 
    P(A), 
    Q(P(A)) 
from tablename 

P et Q ce n'est pas (beaucoup) mieux que l'original, mais si les fonctions sont complexes et n'ont pas beaucoup paramètre, il peut rendre votre déclaration beaucoup plus lisible.

Il vous permet également de tester vos fonctions indépendamment de l'instruction SQL et de toutes les données.

+0

Je n'ai jamais fait de fonctions dans Oracle auparavant, et apparemment je n'ai pas assez de privilèges pour le faire. (Je n'aurai pas besoin de ces requêtes assez souvent pour justifier une fonction à l'échelle de DB.) – BlackShift

0

pourraient vous intéresser cela un peu mieux que l'exemple de vue en ligne que vous avez donné:

WITH inner_view AS (SELECT A*2 AS P FROM tablename) 
SELECT P, P+5 AS Q 
FROM inner_view 

Il revient à la même chose, mais il est un peu plus clair à lire, je pense.

Si la colonne calculée est quelque chose que vous utiliserez dans plusieurs colonnes, il peut être judicieux de créer une vue permanente.

Oracle 11 (que je n'ai pas encore utilisé) a une fonction de colonne virtuelle qui pourrait vous être utile.

+0

C'est plus lisible en effet. Je crois que c'est une base de données 11g, mais je ne suis pas le DBA, ce que je dois être pour les colonnes virtuelles, il semble. – BlackShift

Questions connexes