1

我正在使用 SQL Server 2012 并且有一个包含成员信息的数据库。我正在尝试编写 SQL 来计算每个月存在的每种类型的成员,这样我就可以报告成员资格是否逐月上升/下降。我的架构的简化版本是:

http://www.sqlfiddle.com/#!6/5fed2/1

CREATE TABLE MemberType
(
    MemberTypeID INT,
    Name VARCHAR(50)
);

CREATE TABLE Person
(
    PersonID INT,
    Name VARCHAR(50)
);

CREATE TABLE Invoice
(
    InvoiceID INT,
    DateStart DATE,
    DateEnd   DATE,
    PersonID  INT,     --(FK) Person that purchased the membership
    MemberTypeID INT,  --(FK) Type of membership
);

INSERT MemberType (MemberTypeID,Name) VALUES (1,'Regular')
INSERT MemberType (MemberTypeID,Name) VALUES (2,'Special')
INSERT MemberType (MemberTypeID,Name) VALUES (3,'Honorary')

INSERT Person (PersonID,Name) VALUES (1,'Joe Smith')
INSERT Person (PersonID,Name) VALUES (2,'Anna Smith')
INSERT Person (PersonID,Name) VALUES (3,'Ted Williams')
INSERT Person (PersonID,Name) VALUES (4,'Bill Williams')

INSERT Invoice VALUES(1,'2011-5-1', '2012-1-1', 1, 1)
INSERT Invoice VALUES(2,'2010-1-1', '2013-1-1', 2, 2)
INSERT Invoice VALUES(3,'2012-2-1', '2013-2-1', 3, 1)
INSERT Invoice VALUES(4,'2009-1-1', '2011-5-1', 1, 1)
INSERT Invoice VALUES(5,'2011-1-1', '2012-5-1', 4, 1)

我试图获取特定日期的成员计数,如下所示:

DECLARE @RunDt DATE='2011-1-1'

SELECT
mt.Name [MemberType], @RunDt [Date], count(p.PersonID) [Count]
FROM MemberType mt
LEFT OUTER JOIN Invoice i ON i.MemberTypeID=mt.MemberTypeID
LEFT OUTER JOIN Person p ON p.PersonID=i.PersonID
WHERE @RunDt BETWEEN i.DateStart AND i.DateEnd
GROUP BY mt.Name

我认为这很接近,但我希望将名誉类型包含在计数为 0 中。我认为LEFT OUTER JOINs 可以做到这一点,但你没有。此外,这仅适用于单个日期。我的下一步是将每个月 1 日的查询结果放在一个结果集中。

4

1 回答 1

3

通过将Invoice表的过滤条件放在WHERE子句中,您实际上已将您转换OUTER JOININNER JOIN. 尝试将该过滤条件放在反对的ON条件中。OUTER JOINInvoice

SELECT 
  mt.Name AS [MemberType], 
  @RunDt AS [Date], 
  count(p.PersonID) AS [Count]
FROM dbo.MemberType AS mt
LEFT OUTER JOIN dbo.Invoice AS i 
  ON i.MemberTypeID=mt.MemberTypeID
  AND @RunDt BETWEEN i.DateStart AND i.DateEnd
LEFT OUTER JOIN dbo.Person AS p 
  ON p.PersonID = i.PersonID
GROUP BY mt.Name;

演示:

http://www.sqlfiddle.com/#!6/5fed2/2


如果您想在给定年份的每个月执行此操作:

DECLARE @year INT = 2012;

;WITH x(d) AS 
(
    SELECT CONVERT(DATE, DATEADD(MONTH, n-1, 
      DATEADD(YEAR, @year-1900, '19000101')))
    FROM 
    (
      SELECT TOP (12) n = ROW_NUMBER() OVER 
        (ORDER BY [object_id])
      FROM sys.all_objects 
      ORDER BY [object_id]
    ) AS y
)
SELECT
    mt.[Name],
    [Date] = x.d, 
    [Count] = COUNT(p.PersonID)
FROM x CROSS JOIN dbo.MemberType AS mt
LEFT OUTER JOIN dbo.Invoice AS i 
ON i.MemberTypeID = mt.MemberTypeID
AND x.d BETWEEN i.DateStart AND i.DateEnd
LEFT OUTER JOIN dbo.Person AS p 
ON p.PersonID = i.PersonID
GROUP BY mt.Name, x.d
ORDER BY x.d, mt.Name;

SQLFiddle 演示:

http://sqlfiddle.com/#!6/f5f84/2

有关没有循环的生成集的更多信息:

http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a -set-2
http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-3

于 2013-01-28T02:13:20.090 回答