【问题标题】:SQL SERVER - is there a way to loop a column in a table and use that in the where clause of another select statement?SQL SERVER - 有没有办法循环表中的列并在另一个 select 语句的 where 子句中使用它?
【发布时间】:2021-12-10 08:59:41
【问题描述】:

我正在尝试在我的数据库中获取分层数据。这是我的初始代码。

        SELECT TOP 5
            year, location_state, location_city,
            COUNT(tf.customer_key) Number_of_Customers
        FROM TransactionFact tf
        JOIN LocationDim as ld
            ON ld.location_key = tf.seller_location_key
        JOIN DateDim dd
            ON dd.date_key = tf.order_date_key
        WHERE dd.year = 2016 and location_state = 'SP'
        GROUP BY dd.year, ld.location_state, ld.location_city
        ORDER BY dd.year DESC, Number_of_Customers DESC

这是结果,result

基本上,在查询中,我想做的是不对WHERE 子句中的location_state 进行硬编码。我想让它动态化,以便我得到每个州前 5 个 城市。 p>

这是 LocationDim 表的列名

location_key
location_zip_code_prefix
location_state
location_city

编辑: 我需要的是这样的东西。

+------+----------------+---------------+---------------------+
| year | location_state | location_city | Number_of_Customers |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_1     |    city_1     |       100           |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_1     |    city_2     |       90            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_1     |    city_3     |       89            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_1     |    city_4     |       88            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_1     |    city_5     |       20            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_2     |    city_1     |       100           |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_2     |    city_2     |       45            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_2     |    city_3     |       23            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_2     |    city_4     |       10            |
+------+----------------+---------------+---------------------+
| 2016 |    STATE_2     |    city_5     |       5             |
+------+----------------+---------------+---------------------+



PS:抱歉,这是我在 stackoverflow 中的第一个问题。如果这个问题是重复的,请删除链接,我会试一试。提前谢谢你。

【问题讨论】:

  • 这能回答你的问题吗? Get top 1 row of each group
  • @Charlieface,我想我很难读懂那个,因为我不知道 CTE 是如何工作的。我仍然需要弄清楚这一点。我希望有一个更简单的方法。谢谢
  • 标准答案与@LukStorms 答案几乎相同:只需添加一个分区行号并按此过滤即可。您将整个查询放入带有行号的派生表中,然后在该表之外进行过滤(因为您无法从同一级别过滤ROW_NUMBER)。旁白:COUNT(tf.customer_key)COUNT(*) 相同,它只计算非空行。如果您想要不同 customer_key 的计数,则需要 COUNT(DISTINCT tf.customer_key)
  • @Charlieface 是的,它解决了我的问题。我已经对他的回答投了赞成票,但我不确定为什么他的回答中没有绿色。

标签: sql-server tsql


【解决方案1】:

如果您计算 row_number,则可以对其进行过滤。

SELECT *
FROM 
(
    SELECT 
      dd.[year]
    , ld.location_state
    , ld.location_city
    , COUNT(tf.customer_key) AS total_customers
    , rn = ROW_NUMBER() OVER (PARTITION BY ld.location_state, dd.[year] 
                              ORDER BY COUNT(tf.customer_key) DESC)
    FROM TransactionFact AS tf
    JOIN LocationDim AS ld
      ON ld.location_key = tf.seller_location_key
    JOIN DateDim AS dd
      ON dd.date_key = tf.order_date_key
    WHERE dd.[year] = 2016
    GROUP BY ld.location_state, ld.location_city, dd.[year] 
) q
WHERE rn <= 5
ORDER BY location_state, [year], rn

【讨论】:

  • @LukeStorms 你能解释一下 rn 是如何工作的吗?因为这确实解决了我的问题。
  • 它为每个 PARTITIONend 列生成一个没有间隙的序列号。同样流行的是 DENSE_RANK,它将按顺序为相同的值分配相等的数字。 F.e.具有相同 total_customers 的 2 将获得相同的 dense_rank,但不同的 row_number。
  • @LukeStorms,好的,谢谢。我想我必须阅读更多关于该功能的内容。我已经赞成您的解决方案。谢谢!
  • 它的工作原理基本上是因为window functions这样的东西是在group by之后处理的。
【解决方案2】:

您可以使用ROW_NUMBER() OVER (PARTITION BY ORDER BY )添加行号

下面的查询按location_state分区记录,并以number_of_customer顺序添加行号:

Select * from 
inn.*,  ROW_NUMBER() OVER (PARTITION BY location_state ORDER BY Number_of_Customers DESC) AS rn
( 
SELECT year,
       location_state,
       location_city,
       COUNT(tf.customer_key) Number_of_Customers
  FROM TransactionFact tf
  JOIN LocationDim as ld
    ON ld.location_key = tf.seller_location_key
  JOIN DateDim dd
    ON dd.date_key = tf.order_date_key
 WHERE dd.year = 2016
 GROUP BY dd.year, ld.location_state, ld.location_city
 ) inn

在此之后,您可以轻松过滤并选择top5或任何其他顺序...

注意:我将您的查询用作内部查询。由于没有小提琴,我没有机会测试它

【讨论】:

    【解决方案3】:

    您可以尝试使用 cross apply 运算符将每个 location_state 的值传递给您的查询,如下所示:

    ;With STATES As (
    SELECT location_state 
    FROM LocationDim 
    GROUP BY location_state)
    SELECT T.[year], T.location_state, T.location_city, T.Number_of_Customers
    FROM STATES CROSS APPLY (
                SELECT TOP 5
                    [year], location_state, location_city,
                    COUNT(tf.customer_key) AS Number_of_Customers
                FROM TransactionFact tf
                JOIN LocationDim as ld
                    ON ld.location_key = tf.seller_location_key
                JOIN DateDim dd
                    ON dd.date_key = tf.order_date_key
                WHERE dd.[year] = 2016 and location_state = STATES.location_state
                GROUP BY dd.[year], ld.location_state, ld.location_city
                ORDER BY dd.[year] DESC, Number_of_Customers DESC) As T
    

    【讨论】:

      【解决方案4】:

      所以,基本上这里发生了什么:

          SELECT TOP 5
              dd.[year], ld.location_state, ld.location_city, COUNT(tf.customer_key) Number_of_Customers
          FROM TransactionFact tf
          JOIN LocationDim as ld
              ON ld.location_key = tf.seller_location_key
          JOIN DateDim dd
              ON dd.date_key = tf.order_date_key
          WHERE dd.year = 2016 and location_state = 'SP'
          GROUP BY dd.year, ld.location_state, ld.location_city
          ORDER BY dd.year DESC, Number_of_Customers DESC;
      

      您是否只选择该查询的前 5 个结果。

      但你想要的是得到所有结果,按年份和州排名,并且只取每个州的前 5 名?

      我会使用RANK() function,它是专为您正在查看的场景而设计的。我将在您的查询中将其显示为添加的列:

        SELECT * FROM ( SELECT dd.[year], ld.location_state, ld.location_city, COUNT(tf.customer_key) Number_of_Customers,
                 RANK() OVER(PARTITION BY dd.[year], ld.location_state, ld.location_city 
                             ORDER BY Number_of_Customers DESC) r
          FROM TransactionFact tf
          JOIN LocationDim as ld
              ON ld.location_key = tf.seller_location_key
          JOIN DateDim dd
              ON dd.date_key = tf.order_date_key
          WHERE dd.year = 2016 and location_state = 'SP' 
          GROUP BY dd.year, ld.location_state, ld.location_city
           ) x WHERE x.r <= 5
           ORDER BY x.[year] desc, x.location_state, x.r
      

      或者,您可以在应用RANK 之前使用CTE(公用表表达式)来保存第一个查询的结果:

      ;WITH cte AS(
         SELECT dd.[year], ld.location_state, ld.location_city, COUNT(tf.customer_key) Number_of_Customers, 
             RANK() OVER(PARTITION BY [year], location_state, location_city
                         ORDER BY Number_Of_Customers DESC) r
         FROM TransactionFact tf
              JOIN LocationDim as ld
                  ON ld.location_key = tf.seller_location_key
              JOIN DateDim dd
                  ON dd.date_key = tf.order_date_key
         WHERE dd.year = 2016 and location_state = 'SP'
         GROUP BY dd.year, ld.location_state, ld.location_city
      )
      SELECT *
      FROM cte
      WHERE r <= 5;
      

      作为免责声明,我只是在WITH之前放置一个分号,以表明如果事先有一个不以分号结尾的语句,那么该语句将引发错误。

      编辑:添加,使用RANK 意味着结果按值排序。因此,如果两个城市有 30,000 名客户,他们将获得相同的 RANK 值(类似于他们在一轮高尔夫球中打成平手时在排行榜上所做的事情)。这意味着您将从每个州获得至少 5 个结果 - 如果您想要正好 5 个,而不管绑定值如何,那么您可以以相同的方式使用 ROW_NUMBER

      【讨论】:

      • 它抛出一个错误“无效的列名'r'”。我猜它不知道where 中的“r”,即使 r 是在 RANK() 行之后定义的
      • 刚刚编辑将 RANK 放在内部查询中,将 WHERE 放在外部。
      猜你喜欢
      • 2021-11-14
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-14
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多