2009-02-19 8 views
3

J'ai données dans une table de MSSQL (TableB) où le format [dbo] .tableB.myColumn change après une certaine date ...Combinaison SQL dynamique/conditionnelle?

Je fais simple Inscrivez-vous pour cette table ..

Select [dbo].tableB.theColumnINeed from [dbo].tableA 
left outer join [dbo].tableB on [dbo].tableA.myColumn = [dbo].tableB.myColumn 

Cependant, j'ai besoin de joindre, en utilisant une mise en forme différente, basée sur une colonne de date dans le tableau A ([dbo] .tableA.myDateColumn).

Quelque chose comme ...

Select [dbo].tableB.theColumnINeed from [dbo].tableA 
left outer join [dbo].tableB on [dbo].tableA.myColumn = 
    IF [dbo].tableA.myDateColumn > '1/1/2009' 
     BEGIN 
      FormatColumnOneWay([dbo].tableB.myColumn) 
     END 
    ELSE 
     BEGIN 
      FormatColumnAnotherWay([dbo].tableB.myColumn) 
     END 

Je me demande s'il y a une façon de le faire .. ou une meilleure façon, je ne pense pas à aborder ce ..

Répondre

8
SELECT [dbo].tableB.theColumnINeed 
FROM [dbo].tableA 
LEFT OUTER JOIN [dbo].tableB 
ON [dbo].tableA.myColumn = 
    CASE 
    WHEN [dbo].tableA.myDateColumn <= '1/1/2009' THEN FormatColumnOneWay([dbo].tableB.myColumn) 
    ELSE FormatColumnAnotherWay([dbo].tableB.myColumn) 
    END 
0

De la [dbo] préfixe, je crois que vous utilisez SQL Server. Bien que je n'ai pas beaucoup d'expérience avec elle, vous pouvez convertir les deux champs dans un format de date précise:

select * from tableA 
    Left Outer join tableB 
     On CONVERT(CHAR(8), tableA.myColumn, 112) = CONVERT(CHAR(8), tableB.myColumn, 112) 

La même chose devrait fonctionner sur tous les SGBD, en utilisant les fonctions de formatage de date appropriée.

Je ne connais pas SQL Server, mais dans Oracle, vous pouvez créer un index pour l'expression de jointure.

0

Dans SQL Server que vous souhaitez utiliser un cas comme:

SELECT * 
FROM TableA 
INNER JOIN TableB on TableA.Column= 
CASE WHEN TableA.RecordDate>'1/2/08' 
     THEN FormatCoumn(TableB.Column) 
    ELSE FormatColumnOtherWat(TableB.Column) 
END 
+0

Ma suggestion serait de corriger les données car l'optimiseur ne tiendra pas compte des index avec ces fonctions dans la condition JOIN – SQLMenace

+0

Oui mais parfois vous ne pouvez pas corriger les données ;-) – JoshBerke

+0

C'est la même colonne, je la réparerais, mettre une CHECK CONTRAINT dessus pour que ça ne se reproduise pas car tôt ou tard quelqu'un crierait que la performance est inacceptable et ensuite quoi? – SQLMenace

0

Vous savez que cela est mauvais pour la performance puisque vous ne serez pas en mesure d'utiliser les index droit?

Vous pouvez utiliser une instruction CASE bidouille ou ... vous pouvez aller et corriger les données afin que vous puissiez utiliser l'index et il sera beaucoup plus rapide

0

Eh bien, vous pouvez utiliser un sous-requête au format correctement les données dans l'une des tables avant la jointure.

SELECT 
    newB.columnINeed 
FROM 
    tableA AS A 
LEFT OUTER JOIN (
    SELECT 
    columnINeed 
    , CASE WHEN myColumn > '1/1/2009' THEN FormatColumnOneWay(myColumn) 
    ELSE FormatColumnAnotherWay(myColumn) 
    END AS myColumn 
    FROM 
    tableB 
) AS NewB ON A.myColumn = B.myColumn 

Si les questions de performance, vous pourriez peut-être utilisé une vue indexée (sur la base du sous-requête) au lieu de coder en dur la sous-requête dans la requête globale.

+0

Vous ne pouvez pas être en mesure de le faire. Je remarque que vous formatez B sur la base de A. Je suppose que vous pouvez probablement formater B sans impliquer A, puis faire la jointure? – alphadogg

0

Je suis d'accord qu'une syntaxe CASE serait plus appropriée pour la lecture, bien que je ne sais pas s'il y a une différence significative dans le temps de fonctionnement.

La «bonne» chose à faire, vraiment, est de le refaire et de le faire correctement pour commencer. Vos dates doivent être stockées dans des colonnes datetime, et vous avez probablement beaucoup à gagner en migrant toutes vos dates dans la tableB vers une colonne datetime. Vous pouvez le faire de cette façon, entre autres:

  1. Ajouter une colonne dummie à TableB avec le type datetime.
  2. Exécutez une requête qui extrait la valeur de date de la colonne en cours et la place dans la colonne datetime.
  3. Renommez et supprimez des colonnes pour correspondre à la structure de données précédente.
+0

Vous avez oublié l'étape 4: Passez des semaines ou des mois à rechercher toutes les erreurs générées dans d'autres codes/rapports en supprimant une colonne. – CodeRedick

+0

Faites-le dans une vue. Pourquoi les gens n'utilisent-ils pas plus de vues? L'accès direct à la table est mauvais ... – alphadogg

+0

Eh bien, les valeurs datetime non stockées dans les colonnes datetime sont également mauvaises. En fonction de la taille de l'application utilisant la base de données, il peut y avoir beaucoup de problèmes - oui, mais si vous avez utilisé une bonne séparation des problèmes, vous n'aurez pas beaucoup d'endroits à changer. Pourquoi passer du temps à pirater le code malodorant? –

0

Ok, maintenez. Quel est le type de données réel de la colonne? Je suppose que ce n'est pas DateTime, parce que vous ne contrôlez pas vraiment le formatage ...il stocke juste une date. Peut-il être CAST ou CONVERTed à un DateTime si?

Vous pouvez

left outer join tableb on tableA.myColumn = CAST(tableb.MyColumn as DateTime) 

De cette façon, vous ne correspond pas à une chaîne, mais la date réelle qui devrait être plus fiable. C'est aussi plus simple et plus facile à lire. Les vraies questions sont pourquoi la date n'est pas stockée comme DateTime dans la première place ...

5

Plutôt que d'avoir une instruction CASE dans le JOIN, ce qui empêchera la requête d'utiliser des index, vous pourriez envisager d'utiliser un UNION

SELECT [dbo].tableB.theColumnINeed 
FROM [dbo].tableA 
    LEFT OUTER JOIN [dbo].tableB 
     ON [dbo].tableA.myDateColumn > '1/1/2009' 
     AND [dbo].tableA.myColumn = FormatColumnOneWay([dbo].tableB.myColumn) 
UNION ALL 
SELECT [dbo].tableB.theColumnINeed 
FROM [dbo].tableA 
    LEFT OUTER JOIN [dbo].tableB 
     ON [dbo].tableA.myDateColumn <= '1/1/2009' 
     AND [dbo].tableA.myColumn = FormatColumnAnotherWay([dbo].tableB.myColumn) 

mais si le FormatColumnOneWay/FormatColumnAnotherWay sont des fonctions ou des expressions sur le terrain, qui va probablement exclure l'utilisation de inxdexes sur [mycolumn], bien que tout indice sur myDateColumn doit encore être utilisé

Cependant, il pourrait aider à comprendre ce que la logique FormatColumnOneWay/FormatColumnAnotherWay est, comme connu que peut permettre une meilleure optimisation

Couple de choses à noter:

UNION ALL ne supprimera pas les doublons (contrairement UNION). Parce que les deux sous-requêtes sont mutuellement exclusives, cela est OK et enregistre l'étape SORT que UNION ferait pour lui permettre de supprimer les doublons. Vous ne devez pas utiliser le style '1/1/2009' pour les chaînes de caractères, vous devez utiliser le style 'yyyymmdd' sans les barres obliques ou les traits d'union (vous pouvez également utiliser CONVERT avec un paramètre pour indiquer explicitement que la chaîne est en D/m/y ou m/d/y style

+0

Bons commentaires .. merci. – madcolor