WHERE Column NOT LIKE 不能正确使用递归 cte
WHERE Column NOT LIKE isn't working correctly with recursive cte
这是SQL服务器代码。
假设您有一个包含三列的 table。第 1 列名为 Monster,第 2 列名为 Level,第 3 列名为 BodyType。 Level表示怪物的强大程度,BodyType表示它拥有的body类型。
我的架构:
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,因为 table 中的怪物数量可能会波动(因此我可以稍后添加更多怪物)。该命令还获取正在组合的怪物等级的总和值。该命令也只输出低于某个总和值的怪物组合。在此示例中,总和值为 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 子句来只带回不属于某种 body 类型(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
输出如下,这是不正确的,因为它仍然包含 body 类型的 Slimy 和 Fiery:
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
输出似乎部分起作用,因为 Large Beast 是 Slimy,它第一次忽略了它,但我怀疑它在 BodyType 级别移动时忽略了 NOT LIKE 子句,这就是它不忽略 Large 的原因野兽在随后的发现。
你的逻辑需要AND
,而不是OR
:
WHERE BodyType NOT LIKE 'Fiery' AND BodyType NOT LIKE 'Slimy'
"Fiery" 的 BodyType
不像 "Slimy"。所以,它符合第二个条件。请注意,如果您使用 LIKE
,那么您需要 AND
.
我想我找到了解决办法。输出数据似乎是正确的:
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 子句,只是在原始代码的 CROSS JOIN 之后的 Where 子句中添加了 NOT LIKE。
只是不确定这是否不是最佳做法或者是否会破坏某些东西,有人想插话吗?谢谢。
编辑:我的解决方案有一个问题,即第一次迭代将包括被忽略的 'BodyTypes'
如果我没理解错的话,您可以尝试以下两种方法:
1.Filter血型在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.Use 在递归 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)
这是SQL服务器代码。
假设您有一个包含三列的 table。第 1 列名为 Monster,第 2 列名为 Level,第 3 列名为 BodyType。 Level表示怪物的强大程度,BodyType表示它拥有的body类型。
我的架构:
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,因为 table 中的怪物数量可能会波动(因此我可以稍后添加更多怪物)。该命令还获取正在组合的怪物等级的总和值。该命令也只输出低于某个总和值的怪物组合。在此示例中,总和值为 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 子句来只带回不属于某种 body 类型(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
输出如下,这是不正确的,因为它仍然包含 body 类型的 Slimy 和 Fiery:
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
输出似乎部分起作用,因为 Large Beast 是 Slimy,它第一次忽略了它,但我怀疑它在 BodyType 级别移动时忽略了 NOT LIKE 子句,这就是它不忽略 Large 的原因野兽在随后的发现。
你的逻辑需要AND
,而不是OR
:
WHERE BodyType NOT LIKE 'Fiery' AND BodyType NOT LIKE 'Slimy'
"Fiery" 的 BodyType
不像 "Slimy"。所以,它符合第二个条件。请注意,如果您使用 LIKE
,那么您需要 AND
.
我想我找到了解决办法。输出数据似乎是正确的:
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 子句,只是在原始代码的 CROSS JOIN 之后的 Where 子句中添加了 NOT LIKE。
只是不确定这是否不是最佳做法或者是否会破坏某些东西,有人想插话吗?谢谢。
编辑:我的解决方案有一个问题,即第一次迭代将包括被忽略的 'BodyTypes'
如果我没理解错的话,您可以尝试以下两种方法:
1.Filter血型在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.Use 在递归 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)