选择与列表中的所有项匹配的行组

时间:2013-04-12 17:08:38

标签: sql sql-server tsql relational-division

假设我有两个表:

cars - 汽车列表

carname | modelnumber | ...

passedtest - 包含汽车通过的每项测试:

id | carname | testtype | date | ...
1  | carA    | A        | 2000 |
2  | carB    | C        | 2000 |
3  | carC    | D        | 2001 |
4  | carA    | C        | 2002 |

现在,如何从通过所有测试(A,B,C,D)的passedtest表中选择一辆汽车?

我尝试了IN声明,但它也匹配通过一次测试的汽车。我正在寻找一个语句来匹配所有行中列表中的所有值。

3 个答案:

答案 0 :(得分:39)

这个怎么样?

SELECT carname
FROM PassedTest
GROUP BY carname
HAVING COUNT(DISTINCT testtype) = 4

您还可以将其用作从cars表中获取信息的内部声明:

SELECT *
FROM cars
WHERE carname IN (
    SELECT carname
    FROM PassedTest
    GROUP BY carname
    HAVING COUNT(DISTINCT testtype) = 4
)

答案 1 :(得分:31)

此类问题称为Relational Division

SELECT  a.*
FROM    Cars a
        INNER JOIN
        (
            SELECT  CarName
            FROM    PassedTest 
            WHERE   testType IN ('A', 'B', 'C', 'D')
            GROUP   BY CarName
            HAVING  COUNT(*) = 4
        ) b ON a.CarName = b.CarName

如果UNIQUE对表TestType上的每个CarName强制执行PassedTest约束,则DISTINCT需要COUNT()个关键字,它只会计算唯一值。

SELECT  a.*
FROM    Cars a
        INNER JOIN
        (
            SELECT  CarName
            FROM    PassedTest 
            WHERE   testType IN ('A', 'B', 'C', 'D')
            GROUP   BY CarName
            HAVING  COUNT(DISTINCT TestType) = 4
        ) b ON a.CarName = b.CarName

但如果您只对CARNAME感兴趣,那么您不需要加入表格。查询表PassedTest将符合您的需求。

SELECT  CarName
FROM    PassedTest 
WHERE   testType IN ('A', 'B', 'C', 'D')
GROUP   BY CarName
HAVING  COUNT(*) = 4

答案 2 :(得分:3)

您想要执行关系除法,这是一种未在SQL中实现的操作。以下是我们拥有产品供应商表和必需产品表的示例:

CREATE TABLE product_supplier (
    product_id int NOT NULL,
    supplier_id int NOT NULL,
    UNIQUE (product_id, supplier_id)
);
INSERT INTO product_supplier (product_id, supplier_id) VALUES
(1, 1),
(2, 1),
(3, 1),
(1, 2),
(2, 2),
(3, 2),
(4, 2),
(2, 3),
(3, 3),
(4, 3);

CREATE TABLE reqd (
    product_id int NOT NULL,
    UNIQUE (product_id)
);
INSERT INTO reqd (product_id) VALUES
(1),
(2),
(3);

...我们希望找到所有供应所有必需产品的供应商,也许还有其他供应商。上述示例中的结果将是供应商1和2。

最直接的解决方案是:

SELECT product_supplier.supplier_id
FROM product_supplier
LEFT JOIN reqd ON product_supplier.product_id = reqd.product_id
GROUP BY product_supplier.supplier_id
HAVING COUNT(reqd.product_id) = (SELECT COUNT(*) FROM reqd);
+-------------+
| supplier_id |
+-------------+
|           1 |
|           2 |
+-------------+

如果我们想要找到所有供应所有必需产品的供应商而没有其他供应商(确切划分/没有剩余),那么再添加一个条件:

SELECT product_supplier.supplier_id
FROM product_supplier
LEFT JOIN reqd ON product_supplier.product_id = reqd.product_id
GROUP BY product_supplier.supplier_id
HAVING COUNT(reqd.product_id) = (SELECT COUNT(*) FROM reqd)
AND COUNT(product_supplier.product_id) = (SELECT COUNT(*) FROM reqd);
+-------------+
| supplier_id |
+-------------+
|           1 |
+-------------+

另一种解决方案是重新解决问题:选择供应商提供的产品中不存在所需产品的供应商。嗯:

SELECT DISTINCT supplier_id
FROM product_supplier AS ps1
WHERE NOT EXISTS (
    SELECT *
    FROM reqd
    WHERE NOT EXISTS (
        SELECT *
        FROM product_supplier AS ps2
        WHERE ps1.supplier_id = ps2.supplier_id AND ps2.product_id = reqd.product_id
    )
);
+-------------+
| supplier_id |
+-------------+
|           1 |
|           2 |
+-------------+