我有一个包含三列的表格,其中包含以下值(dbFiddle)
C1 C2 C3
----------------------------
Red Yellow Blue
null Red Green
Yellow null Violet
我尝试创建一个返回包含值"黄色"的所有行的查询。不使用IN
或OR
。如果我执行以下查询:
SELECT 1
FROM test
WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%'
它正确返回指定的行。但是,如果我尝试在exists
:
SELECT *
FROM test
WHERE EXISTS (SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%')
它返回所有行,而不仅仅是"黄色"字。我在这里做错了什么?
非常感谢任何帮助。
答案 0 :(得分:2)
<强> RE 强>
SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%'
“正确返回指定的行”
select返回单个列1
,但这是因为有一行在其文本的某处有一列包含Yellow
。
这是因为EXISTS:
如果子查询包含任何行,则返回TRUE。
即。以下所有查询都返回了test
表中的所有行:
SELECT * FROM test WHERE EXISTS (SELECT 1);
SELECT * FROM test WHERE EXISTS (SELECT 0);
SELECT * FROM test WHERE EXISTS (SELECT NULL);
...只是因为SELECT
至少返回一行!
EXISTS
的通常用法还包括将EXISTS
中的子查询与外部选择相关联。
相关示例
在下面的人为例子中,我们有4个人住在两个房子里。在这里,我们使用EXISTS
来确定幸福者的姓名,并且还有一个其他的人也很幸福生活在同一个(相关)的房子里。
CREATE TABLE House
(
HouseId INT PRIMARY KEY,
Name VARCHAR(MAX)
);
CREATE TABLE Person
(
PersonId INT PRIMARY KEY,
HouseId INT FOREIGN KEY REFERENCES HOUSE(HouseId),
Name VARCHAR(MAX),
IsHappy BIT
);
INSERT INTO House(HouseId, Name) VALUES (1, 'House1'), (2, 'House2');
INSERT INTO Person(PersonId, HouseId, Name, IsHappy) VALUES
(1, 1, 'Joe', 0),
(2, 1, 'Jim', 1),
(3, 2, 'Fred', 1),
(4, 2, 'Mary', 1);
SELECT pOuter.Name
FROM Person pOuter
WHERE pOuter.IsHappy = 1
AND EXISTS
(SELECT 1
FROM Person pInner
WHERE pInner.HouseId = pOuter.HouseId
AND pInner.PersonId != pOuter.PersonId
AND pInner.IsHappy = 1);
返回
Mary
Fred
(显然有其他方法可以找到相同的结果,例如找到House Id的分组,其中有2个或更多幸福的人,等等)
答案 1 :(得分:1)
exists
子句检查指定一个子查询来测试是否存在行。
所以当任何列包含exists (SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%')
数据时,yellow
始终会有行。
如果要使用exists
,则需要通过外部表设置内部exists
查询CONCAT(t.C1, t.C2, t.C3)
。
SELECT *
FROM test t
where exists (SELECT 1 FROM test WHERE CONCAT(t.C1, t.C2, t.C3) LIKE '%Yellow%')
您不需要使用exists
仅在where
上设置条件
SELECT *
FROM test
where CONCAT(C1, C2, C3) LIKE '%Yellow%'
答案 2 :(得分:1)
我会使用cross apply
:
SELECT 1
FROM test t CROSS APPLY
(SELECT COUNT(*) as cnt
FROM (VALLUES (C1), (C2), (C3)) V(C)
WHERE c = 'Yellow'
) v
WHERE cnt > 0;
您可以轻松地将其调整为子查询:
SELECT . . .
FROM test t
WHERE EXISTS (SELECT 1
FROM (VALLUES (C1), (C2), (C3)) V(C)
WHERE c = 'Yellow'
) ;
就个人而言,我更喜欢将每个值直接比较为'Yellow'
,而不是使用LIKE
。例如,这与“黄绿色”或“黄色”是名称的一部分的任何其他值不匹配。
而且,只是为了记录,你仍然可以使用布尔逻辑,即使你不使用OR
和IN
:
where not (coalesce(c1, '') <> 'Yellow' and
coalesce(c2, '') <> 'Yellow' and
coalesce(c3, '') <> 'Yellow'
)
从技术上讲,这可能是解决您问题的“最简单”的解决方案。但是,我仍然更喜欢apply
方法,因为意图更清晰。