2017-05-27 39 views
2

這是SQL Server代碼。WHERE Column NOT LIKE與遞歸cte不能正常工作

比方說,你有一個三列的表。列1被命名爲Monster,列2被命名爲級別,而列3被命名爲BodyType。等級表示怪物的強大程度,BodyType表示它具有何種類型的身體。

我的架構:

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') 
; 

我有一個SQL命令來尋找怪物的所有可能的組合。它使用遞歸cte,因爲表中怪物的數量可能會波動(所以我可以在以後添加更多的怪物)。該命令還獲取正在組合的怪物等級的總和值。該命令還僅輸出落在某個總和值下的怪物組合。在這個例子中,總和值是1500.到目前爲止,所有的東西都能正常工作。

我的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) 

和正確的輸出:

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 

我遇到的問題是,當我添加一個WHERE子句來只帶回那些有一定的不怪物身體類型(BodyType列)。從改性以上時的代碼的一部分來完成,這是:

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

輸出成爲這是不正確以下,因爲它仍然包括主體類型粘糊糊和熾熱的:

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 

輸出似乎是部分工作,因爲大野獸粘糊糊的,它第一次忽略它,但我懷疑它是忽略NOT LIKE子句時,通過BodyType的水平,這就是爲什麼它不會忽略大野獸隨後的發現。

+0

之前過濾'NOT LIKE「Slimy''或'NOT LIKE '%黏滑%''?他們是不同的。 – TriV

+0

@TriV你好TriV,仍然不能解決它。我可能已經找到了解決方案,只需要確認它是乾淨的代碼:) – Kian

+0

你可以試試我的答案... – TriV

回答

2

如果我理解正確的,你可以試試下面的方法2:

1。過濾後的血型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.使用第二CTE遞歸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

第二種解決方案似乎效果很好。 :)第一種解決方案在我使用附加的架構數據測試時丟失了一些輸出值。我認爲它可能不會通過級別2之後的迭代。以下是您的工作解決方案:http://rextester.com/live/UNCPIY56111以下是您的其他解決方案,忽略了許多有效的結果:http://rextester.com/live/PKOVR79091 – Kian

+0

我剛剛看過你的測試數據。我調整了第一個查詢,你可以嘗試http://rextester.com/QZZQVX23133 – TriV

+0

嘿,那有效。但我注意到在服務器端處理需要的時間差不多是三到四倍。我想我會選擇你的第一個工作解決方案。順便說一句,你能給我發電子郵件在kiangraphy [at] gmail.com我只有一個後續問題,我認爲這裏不適合。 – Kian

0

您需要AND在你的邏輯,而不是OR

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

「火熱」 的BodyType是不喜歡 「粘糊糊」。所以,它符合第二個條件。請注意,如果您使用LIKE,那麼您需要AND

+0

這就是我一開始所做的。但我認爲OR會幫助它忽略Fiery和Slimy,如果它同時出現。該解決方案仍然無法工作。如果你不相信我,你應該試試http://rextester.com/l/sql_server_online_compiler。 – Kian

0

我想我找到了解決方案。輸出數據似乎是正確的:

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') 
) 

我拿着破Where子句出來,只是增加了NOT LIKE的WHERE子句的CROSS後,從原來的代碼即可加入。

只是不確定這是不是一個最佳做法,或者可以打破某些東西,任何人想要參加?謝謝。

編輯:我的解決方案有一個問題,在第一次迭代將包括忽略「BodyTypes」