【问题标题】:Cross Join with min and max condition具有最小和最大条件的交叉连接
【发布时间】:2021-03-13 05:46:22
【问题描述】:

我有 customerid 列和每月 billed_date。我想在两者之间创建一个交叉连接,我只会为每个客户的每个 customer_id 从 min(billed_date) 到 max(billed_date) 开具账单日期。 这是一个示例表来解释我想要实现的目标 -

【问题讨论】:

  • 您想要一个dates 表格(或数字表格,您可以使用它来制作dates 表格投影)来填补空白。
  • 我想以任何可能的方式填补空白,实际上我想要一个 Customerid 和计费日期的组合,其中每个客户 id 的日期从该客户的 min(billed_date) 到 max(billed_date)

标签: sql sql-server


【解决方案1】:

您可以像这样使用递归 CTE 获得所需的输出:

with cte as (
  select
    cust_id,
    min(bill_date) as bill_date,
    max(bill_date) as max_bill_date
  from mytable group by cust_id
  union all
  select cust_id, DATEADD(month, 1, bill_date), max_bill_date
  from cte where bill_date < max_bill_date
)
select c.cust_id, c.bill_date, coalesce(m.amount,0)
from cte c left join mytable m
on c.cust_id=m.cust_id and c.bill_date=m.bill_date
order by 1, 2
  1. 使用 MIN 和 MAX 获取每个 cust_id 的周期。
  2. 使用带有 UNION ALL 的递归 CTE 获取每个 cust_id 的所有月份列表。
  3. 加入所有月份和您的表的列表。

【讨论】:

  • 非常感谢!!只是我使用 where bill_date
【解决方案2】:

制作一个日历表(只做一次),这个由 sqlskull.com 提供:

DECLARE @Year INT = '2000';
DECLARE @YearCnt INT = 50 ;
DECLARE @StartDate DATE = DATEFROMPARTS(@Year, 1, 1)
DECLARE @EndDate DATE = DATEADD(DAY, -1, DATEADD(YEAR, @YearCnt, @StartDate));

;WITH Cal(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM Cal
WHERE n < DATEDIFF(DAY, @StartDate, @EndDate)
),
FnlDt(d) AS
(
SELECT DATEADD(DAY, n, @StartDate), n FROM Cal
),
FinalCte AS
(
SELECT
[D] = CONVERT(DATE,d),
[Dy] = DATEPART(DAY, d),
[Mo] = DATENAME(MONTH, d),
[Yr] = DATEPART(YEAR, d),
[DN] = DATENAME(WEEKDAY, d),
N

FROM FnlDt
)
SELECT * 
INTO tally
FROM finalCte
ORDER BY [Date]
OPTION (MAXRECURSION 0);

现在您有了一个可以永远使用的日期和数字表,如下所示:

    SELECT *
    FROM
      (SELECT
          cust_id,
          min(bill_date) minbill,
          max(bill_date) maxbill
      FROM t
    ) r
    INNER JOIN tally x ON x.Dy = DAY(minbill) AND x.d BETWEEN r.minbill AND r.maxbill
    LEFT JOIN t ON x.d = t.bill_date and r.cust_id = t.cust_id

内部连接到计数表的分组查询是您的交叉连接(它不能是交叉连接,因为它确实有一些谓词,即计数行介于 min 和 max 之间)。这将导致每个按客户分组的行乘以最小值和最大值之间的计数中的日期数 - 它为您提供了每个客户 ID 与最小值和最大值之间的每个日期的交叉连接。

然后,您只需使用左连接将其连接回 cust 数据表,以获取有数据的日期或没有数据的日期(并为那些没有数据的日期保留统计日期任何客户数据 - 将 null 转换为 0)

【讨论】:

    【解决方案3】:

    递归 CTE 是解决问题的正确方法。但是,无需连接回原始表。递归 CTE 可以完成所有工作:

    with cte as (
          select cust_id, bill_date, amount, 
                 dateadd(month, -1,
                         lead(bill_date) over (partition by cust_id order by bill_date)
                        ) as next_bill_date
          from t
          union all
          select cust_id, date_add(month, 1, bill_date), 0,
                 next_bill_date
          from cte
          where bill_date < next_bill_date
         )
    select *
    from cte
    order by cust_id, bill_date;
    

    基本上,这会根据需要为每个现有行构造额外的行。 lead() 获取结束日期,递归 CTE 只是添加到行中。我希望它有更好的性能——lead() 应该比group by 有更好的性能,它完全消除了join

    【讨论】:

      【解决方案4】:

      如果您只想处理表格中的日期,您可以使用这种方式,

      首先创建一个包含表中所有日期的表,然后与表交叉连接:

      SELECT DISTINCT bill_date INTO #dates FROM YourTable;
      
      SELECT t.Cust_id,
             t.bill_date,
             MAX(t.Amount) Amount
      FROM
      (
          SELECT Cust_id,
                 bill_date = CASE
                                 WHEN #dates.bill_date <> YourTable.bill_date THEN
                                     #dates.bill_date
                                 ELSE
                                     YourTable.bill_date
                             END,
                 Amount = CASE
                              WHEN #dates.bill_date <> YourTable.bill_date THEN
                                  0
                              ELSE
                                  YourTable.Amount
                          END
          FROM YourTable,
               #dates
      ) t
      GROUP BY t.Cust_id,
               t.bill_date
      ORDER BY t.Cust_id,
               t.bill_date;
      

      【讨论】:

        猜你喜欢
        • 2019-01-17
        • 2014-06-24
        • 1970-01-01
        • 1970-01-01
        • 2013-10-15
        • 2014-10-31
        • 2020-09-09
        • 1970-01-01
        • 2012-11-28
        相关资源
        最近更新 更多