2017-05-27 1 views
2

Il s'agit du code SQL Server.WHERE Colonne NOT LIKE ne fonctionne pas correctement avec le code récursif

Disons que vous avez une table avec trois colonnes. La colonne 1 s'appelle Monster, la colonne 2 s'appelle Level et la colonne 3 BodyType. Le niveau indique à quel point le monstre est puissant et BodyType indique le type de corps qu'il possède.

Mon schéma:

CREATE TABLE YourTable 
    ([Monster] nvarchar(max), [Level] int, [BodyType] nvarchar(max)) 
; 

INSERT INTO YourTable 
    ([Monster], [Level], [BodyType]) 
VALUES 
    ('Small Beast', 300, 'Scaly'), 
    ('Large Beast', 700, 'Slimy'), 
    ('Small Dragon', 350, 'Fiery'), 
    ('Large Dragon', 800, 'Slimy') 
; 

J'ai une commande SQL pour trouver toutes les combinaisons possibles des monstres. Il utilise une cte récursive parce que le nombre de monstres dans la table peut fluctuer (donc je peux ajouter plus de monstres plus tard). La commande récupère également la valeur totale du niveau des monstres qui sont combinés. La commande ne sort également que les combinaisons de monstres qui tombent sous une certaine somme totale. Dans cet exemple, la somme totale est 1500. Tout fonctionne comme il se doit.

Ma commande sql:

;WITH cte AS (
SELECT Monster, 
     [Level], 
     BodyType, 
     1 as l 
FROM YourTable 
UNION ALL 


SELECT c1.Monster+','+c2.Monster, 
     c1.[Level]+c2.[Level], 
     c1.BodyType+','+c2.BodyType, 
     c1.l+1 
FROM cte c1 
CROSS JOIN YourTable c2 
WHERE c1.Monster NOT LIKE '%'+c2.Monster+'%' 
) 


SELECT * 
FROM cte 
WHERE cte.Level < 1500 
ORDER BY l 
OPTION (MAXRECURSION 0) 

Et la sortie correcte:

1 Small Beast 300 Scaly 1 
2 Large Beast 700 Slimy 1 
3 Small Dragon 350 Fiery 1 
4 Large Dragon 800 Slimy 1 
5 Large Dragon,Small Beast 1100 Slimy,Scaly 2 
6 Large Dragon,Small Dragon 1150 Slimy,Fiery 2 
7 Small Dragon,Small Beast 650 Fiery,Scaly 2 
8 Small Dragon,Large Beast 1050 Fiery,Slimy 2 
9 Small Dragon,Large Dragon 1150 Fiery,Slimy 2 
10 Large Beast,Small Beast 1000 Slimy,Scaly 2 
11 Large Beast,Small Dragon 1050 Slimy,Fiery 2 
12 Small Beast,Large Beast 1000 Scaly,Slimy 2 
13 Small Beast,Small Dragon 650 Scaly,Fiery 2 
14 Small Beast,Large Dragon 1100 Scaly,Slimy 2 
15 Small Beast,Large Dragon,Small Dragon 1450 Scaly,Slimy,Fiery 3 
16 Small Beast,Small Dragon,Large Beast 1350 Scaly,Fiery,Slimy 3 
17 Small Beast,Small Dragon,Large Dragon 1450 Scaly,Fiery,Slimy 3 
18 Small Beast,Large Beast,Small Dragon 1350 Scaly,Slimy,Fiery 3 
19 Large Beast,Small Dragon,Small Beast 1350 Slimy,Fiery,Scaly 3 
20 Large Beast,Small Beast,Small Dragon 1350 Slimy,Scaly,Fiery 3 
21 Small Dragon,Large Dragon,Small Beast 1450 Fiery,Slimy,Scaly 3 
22 Small Dragon,Large Beast,Small Beast 1350 Fiery,Slimy,Scaly 3 
23 Small Dragon,Small Beast,Large Beast 1350 Fiery,Scaly,Slimy 3 
24 Small Dragon,Small Beast,Large Dragon 1450 Fiery,Scaly,Slimy 3 
25 Large Dragon,Small Dragon,Small Beast 1450 Slimy,Fiery,Scaly 3 
26 Large Dragon,Small Beast,Small Dragon 1450 Slimy,Scaly,Fiery 3 

Le problème que je rencontrais est quand j'ajoute une clause Where pour faire que de retour des monstres qui ne sont pas d'un certain type de corps (colonne BodyType). Cette partie du code de ci-dessus lorsqu'il est modifié pour y arriver est:

;WITH cte AS (
SELECT Monster, 
     [Level], 
     BodyType, 
     1 as l 
FROM YourTable 
WHERE BodyType NOT LIKE 'Fiery' AND BodyType NOT LIKE 'Slimy' 
UNION ALL 

La sortie est reformulé comme suit ce qui est incorrect, car il comprend encore les types de corps de gluant et Ardent:

Monster Level BodyType l 
1 Small Beast 300 Scaly 1 
2 Small Beast,Large Beast 1000 Scaly,Slimy 2 
3 Small Beast,Small Dragon 650 Scaly,Fiery 2 
4 Small Beast,Large Dragon 1100 Scaly,Slimy 2 
5 Small Beast,Large Dragon,Small Dragon 1450 Scaly,Slimy,Fiery 3 
6 Small Beast,Small Dragon,Large Beast 1350 Scaly,Fiery,Slimy 3 
7 Small Beast,Small Dragon,Large Dragon 1450 Scaly,Fiery,Slimy 3 
8 Small Beast,Large Beast,Small Dragon 1350 Scaly,Slimy,Fiery 3 

La sortie semble fonctionner partiellement puisque Large Beast est Slimy et il l'a ignoré la première fois mais je soupçonne qu'il ignore la clause NOT LIKE en passant par les niveaux de BodyType et c'est pourquoi il n'ignore pas Large Beast lors des découvertes suivantes.

+0

NOT LIKE « Slimy'' ou NOT LIKE '% Visqueux%''? Ils sont différents. – TriV

+0

@ TriV Hi TriV, qui ne le résout toujours pas. J'ai peut-être trouvé une solution, juste besoin de confirmation c'est le code propre :) – Kian

+0

Vous pouvez essayer ma réponse ... – TriV

Répondre

2

Si je comprends bien, vous pouvez essayer 2 approches ci-dessous:

1.Filtre Bloodtype après recursive cte

;WITH cte AS (
SELECT Monster, 
     [Level], 
     BodyType, 
     1 as l 
FROM YourTable 
UNION ALL 

SELECT c1.Monster+','+c2.Monster, 
     c1.[Level]+c2.[Level], 
     c1.BodyType+','+c2.BodyType, 
     c1.l+1 
FROM cte c1 
CROSS JOIN YourTable c2 
WHERE c1.Monster NOT LIKE '%'+c2.Monster+'%' 
) 
SELECT * 
FROM cte 
WHERE cte.Level < 1500 
AND ',' + cte.BodyType ',' + NOT LIKE '%,Fiery,%' 
AND ',' + cte.BodyType ',' + NOT LIKE '%,Slimy,%' 
ORDER BY l 
OPTION (MAXRECURSION 0) 

2.Utiliser la deuxième cte de filtrage récursif avant cte

;WITH temp AS 
(
    SELECT * 
    FROM YourTable 
    WHERE BodyType != 'Fiery' 
      AND BodyType != 'Slimy' 
) 
,cte AS (
SELECT Monster, 
     [Level], 
     BodyType, 
     1 as l 
FROM temp 
UNION ALL 

SELECT c1.Monster+','+c2.Monster, 
     c1.[Level]+c2.[Level], 
     c1.BodyType+','+c2.BodyType, 
     c1.l+1 
FROM cte c1 
CROSS JOIN temp c2 
WHERE c1.Monster NOT LIKE '%'+c2.Monster+'%' 
) 
SELECT * 
FROM cte 
WHERE cte.Level < 1500 
ORDER BY l 
OPTION (MAXRECURSION 0) 
+0

La deuxième solution semble fonctionner très bien. :) La première solution manquait certaines valeurs de sortie lorsque je l'ai testé avec des données de schéma supplémentaires ajoutées. Je pense qu'il ne peut pas passer par des itérations après le niveau 2. Voici votre solution de travail: http://rextester.com/live/UNCPIY56111 Voici votre autre solution qui ignore beaucoup de résultats valides: http://rextester.com/live/PKOVR79091 – Kian

+0

Je viens de voir vos données de test. Je tweak la première requête et vous pourriez l'essayer http://rextester.com/QZZQVX23133 – TriV

+0

Hey cela fonctionne. Mais j'ai remarqué qu'il faut presque trois à quatre fois plus de temps pour traiter du côté serveur. Je pense que je vais aller avec votre première solution de travail. Btw, pouvez-vous m'envoyer un email à kiangraphy [at] gmail.com J'ai juste une question de suivi que je ne pense pas appropriée ici. – Kian

0

Vous avez besoin AND dans votre logique, non OR:

WHERE BodyType NOT LIKE 'Fiery' AND BodyType NOT LIKE 'Slimy' 

Un BodyType de "Fiery" est pas comme "gluant". Donc, cela correspond à la deuxième condition. Notez que si vous utilisez LIKE, vous voulez AND.

+0

Voilà ce que je l'avais comme au début. Mais je pensais que OU l'aiderait à ignorer à la fois Fiery et Slimy si elle apparaissait en même temps. La solution ne fonctionne toujours pas à votre façon. Vous devriez l'essayer sur http://rextester.com/l/sql_server_online_compiler si vous ne me croyez pas. – Kian

0

Je pense avoir trouvé une solution. Les données de sortie semble être correct:

SELECT c1.Monster+','+c2.Monster, 
     c1.[Level]+c2.[Level], 
     c1.BodyType+','+c2.BodyType, 
     c1.l+1 
FROM cte c1 
CROSS JOIN YourTable c2 
WHERE (c1.Monster NOT LIKE '%'+c2.Monster+'%') AND (c1.BodyType NOT LIKE 'Scaly' AND c2.BodyType NOT LIKE 'Scaly') AND (c1.BodyType NOT LIKE 'Fiery' AND c2.BodyType NOT LIKE 'Fiery') 
) 

Je pris la clause Where cassé et juste ajouté NOT LIKE de la clause Where après la CROSS JOIN du code d'origine.

Juste ne sais pas si ce n'est pas une bonne pratique ou pourrait casser quelque chose, quelqu'un veut intervenir? Je vous remercie.

Edit: Ma solution a un problème où la première itération inclura ignoré 'BodyTypes'