【问题标题】:How to select even non existent values如何选择甚至不存在的值
【发布时间】:2011-03-21 01:27:38
【问题描述】:

我使用的是 SQL-Server 2005

我有两个表,用户和订单。每个用户可以有多个订单。通过 userID 连接的表。订单有日期列(即下订单的时间)。用户有registrationSite栏(这是用户及其所有订单背后的附属网站)。

我想选择每天和网站的订单总和,所以 即使网站没有售出或用户注册,我也会有 0 作为总价值。

这是缺少突出显示部分的当前查询。

select sum(orderSum)*40/100-0.17,count(*),
registrationSite,
dateadd(dd,datediff(dd,0,cu.date),0)
from Users cu 
inner join Orders cp 
on cu.userID=cp.userID
group by dateadd(dd,datediff(dd,0,cu.date),0),registrationSite
order by dateadd(dd,datediff(dd,0,cu.date),0),registrationSite

【问题讨论】:

  • 您是否有一个表格来定义可能的注册站点的完整列表?
  • @Tom,不。设计是Users表有varchar列保存域名。

标签: sql sql-server database tsql


【解决方案1】:

使用LEFT OUTER JOIN 而不是INNER JOIN ..

from Users cu 
inner join Orders cp 

应该变成

from Users cu 
left outer join Orders cp 

这意味着所有用户及其附带的订单(如果存在)。(但无论订单如何,用户都将是所有用户


更新

您需要创建一个 tally 表,其中包含日期范围()和left outer join ..

看这里:How to get a table of dates between x and y in sql server 2005

【讨论】:

  • 如果没有用户在某个日期注册离开加入对我没有帮助。
  • 感谢您,但我试图避免这种解决方案,但如果没有其他更好的答案,我将使用它...在您的链接中使用 CTE 的巧妙技巧
【解决方案2】:

如果您使用的是 SQL 2005 或更高版本,则可以使用公用表表达式动态构建日历表。在您的原始帖子中,您正在对 cu.date 进行日期数学运算,但别名 cu 代表的是用户表而不是订单表。我假设这应该是cp.date?即,您的描述谈到了订单日期,但您的 SQL 使用了用户表中的日期。

With OrderDateBoundaries As
    (
    Select Cast(DateDiff(d,0,Min([Date]))) As MinDate
        , Cast(DateDiff(d,0,Max([Date]))) As MaxDate
    From Orders
    )
    , Calendar As
    (
    Select MinDate As [Date]
    From OrderDateBoundaries
    Union All
    Select DateAdd(d, 1, [Date])
    From Calendar
    Where [Date] <= DateAdd(d, 1, (
                                    Select MaxDate
                                    From OrderDateBoundaries
                                    ))
    )
Select Calendar.[Date]
    , Coalesce(Sum(O.ordersum) * 40 / 100 - 0.17,0) As OrderSum
    , Count(*)
    , RegistrationSite
From Calendar
        Left Join (Users As U
            Join Orders As O
                On O.userId = U.UserId)
             On Cast(DateDiff(d, 0, O.OrderDate) As datetime) =Calendar.Date
Group By Calendar.[Date], registrationsite
Option(MaxRecursion 0);

如果您确实想加入Users.Date,那么只需进行简单更改即可。另外,不清楚ordersum是从哪个表中存储的。

With DateBoundaries As
    (
    Select Cast(DateDiff(d,0,Min([Date]))) As MinDate
        , Cast(DateDiff(d,0,Max([Date]))) As MaxDate
    From Users
    )
    , Calendar As
    (
    Select MinDate As [Date]
    From DateBoundaries
    Union All
    Select DateAdd(d, 1, [Date])
    From Calendar
    Where [Date] <= DateAdd(d, 1, (
                                    Select MaxDate
                                    From DateBoundaries
                                    ))
    )
Select Calendar.[Date]
    , Coalesce(Sum(O.ordersum) * 40 / 100 - 0.17,0) As OrderSum
    , Count(*)
    , RegistrationSite
From Calendar
        Left Join (Users As U
            Join Orders As O
                On O.userId = U.UserId)
             On Cast(DateDiff(d,0,U.Date) As datetime) =Calendar.Date
Group By Calendar.[Date], RegistrationSite
Option(MaxRecursion 0);

【讨论】:

  • @Thomas,首先感谢您的正确回复。 OrderSum 存储在 Orders 表中。
  • @Thomas, cu 或 cp on date 给出了相同的结果,因为每天都有一个用户注册,就像至少有一个订单一样。所以在这种情况下没关系,因为每一天至少有一条记录出现在两个表中,尽管你是对的语义。
  • @eugeneK - 我已经修复了两个错别字和一个疏忽。我在 Min([Date]) 上缺少一个结束括号,并且我有两次 MaxDate 的别名。疏忽没有使用日历 CTE 所需的 MaxRecursion 选项。如果你不使用这个并且天数超过了默认的 100 个递归周期,它会抛出一个错误。
  • @Thomas,感谢您的修复,事实上我自己修复了这些问题,但主要错误仍然相同...消息 156,第 15 级,状态 1,第 27 行关键字附近的语法不正确'组”。
  • @eugeneK - 我的另一个错字。问题是缺少一个 ON 子句,我不小心将其合并到AND U.Date =Calendar.Date 中。已更正这两个查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多