【问题标题】:Select group of rows consisting of exactly one specific item选择仅由一个特定项目组成的行组
【发布时间】:2021-12-31 21:48:19
【问题描述】:

我正在进行一项分析,我想获得在我的item table中购买的特定产品。假设我有这些数据:

ProductId ProductName OrderId
1 Tshirt 1
4 Lipstick 1
4 Lipstick 2
6 Jeans 3
4 Lipstick 3
4 Lipstick 4
1 Tshirt 5
6 Jeans 5
4 Lipstick 5

仅购买口红的订单如何获得完整的订单?

输出应该是这样的:

ProductId ProductName OrderId
4 Lipstick 2
4 Lipstick 4

【问题讨论】:

  • 根据问题指南,请展示您的尝试并告诉我们您发现了什么(在本网站或其他地方)以及为什么它不能满足您的需求。
  • 我们选择而不是项目。小心你如何表达你的问题和你的假设。如果订单包含许多行(可能不同)的口红,那么您的结果集包含/代表什么?您假设“订单”与某些答案一样具有单行口红。您的样本数据与该假设一致。当心 - 现实生活从未如此“整洁”。
  • @SMor,我需要一个订单,其中有一个购买,即口红,如果订单包含此项目和许多其他项目,它可以存储在不同的行号下。因此,我很想得到这个特定的项目,并且知道这里要问什么。谢谢

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

您可以检查是否有任何订单项目,不存在其他项目:

SELECT *
FROM t AS t1
WHERE ProductName = 'Lipstick'
AND NOT EXISTS (
    SELECT *
    FROM t AS t2
    WHERE t2.OrderId = t1.OrderId
    AND t2.ProductName <> 'Lipstick'
)

说明:外部查询选择了所有口红订单项。这将返回订单 #1、2、3、4 和 5。让我们将这些行称为口红订单项目。

然后,子查询对于口红订单项中的每一行,选择不包含口红 (t2.ProductName 'Lipstick') 的相关订单项 (t2.OrderId = t1.OrderId)。如果不存在这样的行,则口红订单项是唯一的。

【讨论】:

  • 嗨@Salman A,您是否介意分享一些有关此查询的输出及其工作原理的详细信息:)?
  • 我已经添加了解释。
【解决方案2】:

获取包含口红的订单列表的一种方法:

SELECT OrderId
FROM   Item
GROUP
    BY OrderId
HAVING Min(ProductName) = 'Lipstick'
AND    Max(ProductName) = 'Lipstick'
;

【讨论】:

    【解决方案3】:

    仅检索上一行产品和当前产品相同的产品。这就是为什么使用 lag() 函数来获取前一行产品的原因。

    SELECT t.ProductId, t.ProductName, t.OrderId 
    FROM (SELECT *
               , LAG(ProductId) OVER (ORDER BY OrderId) prev_product
          FROM item) t
    WHERE t.ProductId = t.prev_product
        AND t.ProductName = 'Lipstick'
    

    使用先前和当前行顺序的另一种方式

    SELECT t.ProductId, t.ProductName, t.OrderId 
    FROM (SELECT *
               , LAG(OrderId) OVER (ORDER BY OrderId) prev_order
          FROM item) t
    WHERE t.OrderId != t.prev_order
        AND t.ProductName = 'Lipstick'
    

    请查看网址https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=8cf359bb9bd73fd9fc9b4726117ff872

    根据数据检索甚至orderid。

    SELECT *
    FROM item
    WHERE ProductName = 'Lipstick'
        AND OrderId % 2 = 0
    

    注意:但我只推荐第一个选项。

    【讨论】:

    • 对有适当解释的反对票总是很感激。
    【解决方案4】:

    另一种选择,使用条件聚合

    SELECT i.OrderId
    FROM Item i
    GROUP BY
      i.OrderId
    HAVING COUNT(CASE WHEN ProductName = 'Lipstick' THEN 1 END) > 1
      AND COUNT(CASE WHEN ProductName <> 'Lipstick' THEN 1 END) = 0;
    

    【讨论】:

    • 是的,已经修改
    【解决方案5】:

    您可以构造查询,使其可以显示包含单个项目的所有订单,然后您可以决定检索哪个项目,假设该项目并不总是“口红”。此外,现实生活中的数据库通常很复杂,您可能有一个包含多行单个项目的 OrderId。在这种情况下,您将需要一个不同的计数。这可以通过多种方式完成,但会显示两个选项。

    准备数据库

    CREATE TABLE tablename (ProductId INTEGER, 
                            ProductName VARCHAR(100), 
                            OrderId INTEGER);
    
    INSERT INTO tablename VALUES
      (1, 'Tshirt', 1),
      (4, 'Lipstick', 1),
      (4, 'Lipstick', 2),
      (6, 'Jeans', 3),
      (4, 'Lipstick', 3),
      (4, 'Lipstick', 4),
      (1, 'Tshirt', 5),
      (6, 'Jeans', 5),
      (4, 'Lipstick', 5);
    

    选项 1:使用交叉应用

    WITH Product_count AS (
    SELECT t1.*, t2.cnt
    FROM tablename t1 CROSS APPLY
         (SELECT COUNT(DISTINCT t2.ProductName) AS cnt
          FROM tablename t2
          WHERE t2.OrderId = t1.OrderId 
         ) t2
    )
    SELECT ProductId, ProductName, OrderId
    FROM Product_count
    WHERE cnt = 1 AND ProductName = 'Lipstick';
    

    选项 2:使用窗口函数

    WITH Product_count AS (
    SELECT *,
         DENSE_RANK() OVER (PARTITION BY OrderId ORDER BY ProductName)  
         + DENSE_RANK() OVER (PARTITION BY OrderId ORDER BY ProductName DESC) - 1 AS cnt
    FROM tablename
    )
    SELECT ProductId, ProductName, OrderId
    FROM Product_count
    WHERE cnt = 1 AND ProductName = 'Lipstick';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-14
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      • 2018-04-08
      • 2019-11-17
      相关资源
      最近更新 更多