【问题标题】:Select group of rows that match all items in a list选择与列表中的所有项目匹配的行组
【发布时间】:2013-04-05 07:42:30
【问题描述】:

假设我有两张桌子:

cars – 汽车列表

carname | modelnumber | ...

passedtest – 包含汽车通过的所有测试:

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

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

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

【问题讨论】:

    标签: sql sql-server tsql relational-division


    【解决方案1】:

    您要执行关系除法,这是一种在 SQL 中未实现的操作。这是一个示例,其中我们有一个 product-supplier 表和一个 required-products 表:

    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 |
    +-------------+
    

    【讨论】:

    • 这应该是公认的答案,因为它不需要硬编码值
    【解决方案2】:

    这个怎么样?

    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
    )
    

    【讨论】:

    • 谢谢,但我认为我的输出是重复的,因为要创建汽车表,我使用了内部连接
    • 但是,这不适用于通配符,因为在这种情况下无法建立显式计数。
    • 但是我们正在硬编码 4 的值...如果我们添加一个测试呢?
    【解决方案3】:

    这类问题称为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
    

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

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-14
      • 1970-01-01
      • 2021-09-07
      • 2019-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-30
      相关资源
      最近更新 更多