按类型计算一系列日期的成员资格

时间:2013-01-28 02:09:22

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

我正在使用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 JOIN会实现这一目标,但你不会。 这也仅适用于单个日期。我的下一步是将每个月的第1个查询的结果全部放在一个结果集中。

1 个答案:

答案 0 :(得分:3)

通过在Invoice子句中放置WHERE表的过滤条件,您基本上已将OUTER JOIN转换为INNER JOIN。请尝试将此过滤条件放在ON OUTER JOIN InvoiceSELECT 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; 的{​​{1}}条件中。

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;

演示:

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


如果你想在某一年的每个月都这样做:

{{1}}

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