选择相关表中具有特定行数的行数

时间:2011-12-30 16:35:40

标签: sql sql-server tsql

所以我不太清楚为什么今天早上这个给我这么多麻烦。我打算把它归结为星期五早上。 :)我们的目标是按频率计算该时期内拥有零照片,三张照片和六张或更多照片的房地产物业数量。我需要一个看起来像这样的结果集(注意:“三列或更多列不应该包含正好六列的那些 - 最多可以有六张照片):

Period Start      Period End        Zero      ThreeOrMore     ExactlySix
------------------------------------------------------------------------
1/1/2011          1/7/2011          15        132             512
1/8/2011          1/14/2011         44        123             402

我提出了日期,我实际上已经生成了正确的周开始/结束日期。

我的“属性”表格如下:

PropertyRecId    Other fields
-----------------------------
12345            <blah>
56789            <blah>

我的“PropertyPhoto”表格如下(仅包含必要的字段):

PropertyPhotoId   PropertyRecId     CreatedOn
---------------------------------------------
1                 12345             3/1/2011
2                 12345             3/1/2011
etc...

我正在使用下面的查询,但我并不感激它。我只需要完成目标。请记住,使用我现有的查询,我无法访问顶级选择中的“Propery”记录。我尝试将查询放入创建PeriodStart和PeriodEnd的辅助选择中,但这只会导致不同的问题。

DECLARE @DateStart datetime
DECLARE @DateEnd datetime
SET @DateStart = '1/1/2011'
SET @DateEnd = '12/31/2011'

DECLARE @Frequency varchar(50)
SET @Frequency = 'month'

SELECT
PeriodStart,
PeriodEnd,
??? As Zero,
??? As ThreeOrMore,
??? As ExactlySix
FROM (
SELECT
PeriodStart = CASE @Frequency
WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, CreatedOn))
WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, CreatedOn), CreatedOn)))
WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn), 0)))
WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn), 0)))
WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn), 0)))
END,
PeriodEnd   = CASE @Frequency
WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, CreatedOn)))
WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, CreatedOn), CreatedOn))))
WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn) + 1, 0)))))
WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn) + 1, 0)))))
WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn) + 1, 0)))))
END     
FROM Property P
WHERE CreatedOn BETWEEN @DateStart AND @DateEnd
) s
GROUP BY
PeriodStart,
PeriodEnd
ORDER BY PeriodStart

2 个答案:

答案 0 :(得分:4)

您可以使用子查询中的over函数来获取此函数,并在外部选择case语句中添加'em:

DECLARE @Frequency varchar(50)
SET @Frequency = 'month'

SELECT
    PeriodStart,
    PeriodEnd,
    SUM(case when s.PhotoCount = 0 then 1 else 0 end) As Zero,
    SUM(case when s.PhotoCount between 3 and 5 then 1 else 0 end) As ThreeOrMore,
    SUM(case when s.PhotoCount = 6 then 1 else 0 end) As ExactlySix
FROM (
    SELECT
        PeriodStart = 
        CASE @Frequency
            WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, CreatedOn))
            WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, CreatedOn), CreatedOn)))
            WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn), 0)))
            WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn), 0)))
            WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn), 0)))
        END,
        PeriodEnd   = 
        CASE @Frequency
            WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, CreatedOn)))
            WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, CreatedOn), CreatedOn))))
            WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn) + 1, 0)))))
            WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn) + 1, 0)))))
            WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn) + 1, 0)))))
        END,
        p.PropertyRecId,
        COUNT(ph.PropertyPhotoId) over (partition by p.PropertyRecId) as PhotoCount
    FROM
        Property P
        left join PropertyPhoto ph on
            p.PropertyRecId = ph.PropertyRecId
    WHERE 
        CreatedOn BETWEEN @DateStart AND @DateEnd
) s
GROUP BY
    PeriodStart,
    PeriodEnd
ORDER BY 
    PeriodStart

您也可以使用传统的group by进行内部查询,但我爱我一些over并希望引起您的注意:

SELECT
    PeriodStart = 
    CASE @Frequency
        WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, CreatedOn))
        WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, CreatedOn), CreatedOn)))
        WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn), 0)))
        WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn), 0)))
        WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn), 0)))
    END,
    PeriodEnd   = 
    CASE @Frequency
        WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, CreatedOn)))
        WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, CreatedOn), CreatedOn))))
        WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, CreatedOn) + 1, 0)))))
        WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, CreatedOn) + 1, 0)))))
        WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, CreatedOn) + 1, 0)))))
    END,
    p.PropertyRecId,
    COUNT(ph.PropertyPhotoId) over (partition by p.PropertyRecId) as PhotoCount
FROM
    Property P
    left join PropertyPhoto ph on
        p.PropertyRecId = ph.PropertyRecId
WHERE 
    CreatedOn BETWEEN @DateStart AND @DateEnd
GROUP BY
    p.PropertyRecId,
    p.CreatedOn

答案 1 :(得分:1)

这有效吗?:

--1 : Make a lookup-up table of period start/end
CREATE TABLE #PERIODS (
    [Period Start] datetime
  , [Period End]   datetime
);

DECLARE @firstDay datetime
DECLARE @rowid int
SET @firstDay = '2011-01-01'
WHILE @firstDay < '2012-01-01'
BEGIN
    INSERT INTO #PERIODS VALUES ( @firstDay, DATEADD(DAY,6,@firstDay));
    SET @firstDay = @firstDay + 7
END
GO

-- 2 Count the photos per property per period
CREATE TABLE #PHOTOCOUNT (
    [Period Start] datetime
  , [Period End]   datetime
  , PropertyRecId  int
  , PhotoCount     int
);
GO

INSERT INTO #PHOTOCOUNT
SELECT 
    PD.[Period Start]
  , PD.[Period End]   
  , Property.PropertyRecId
  , COUNT(PropertyPhoto.PropertyPhotoId) AS PhotoCount
FROM 
    #PERIODS AS PD,
    Property
    LEFT JOIN PropertyPhoto
        ON Property.PropertyRecId = PropertyPhoto.PropertyRecId 
WHERE 
    PropertyPhoto.PropertyRecId Is Null
    OR PropertyPhoto.CreatedOn BETWEEN PD.[Period Start] AND PD.[Period End]
GROUP BY 
    PD.[Period Start]
  , PD.[Period End]   
  , Property.PropertyRecId;
GO

-- 3 Categorize by 0, 3 to 5, 6
SELECT 
    [Period Start]
  , [Period End]
  , SUM(RANGES.Zero) AS Zero
  , SUM(RANGES.ThreeToFive) AS ThreeToFive
  , SUM(RANGES.Six) AS Six
FROM (
    SELECT 
        [Period Start]
      , [Period End]
      , Zero = CASE WHEN PhotoCount = 0 THEN 1 ELSE 0 END 
      , ThreeToFive = CASE WHEN PhotoCount BETWEEN 3 AND 5 THEN 1 ELSE 0 END 
      , Six = CASE WHEN PhotoCount = 6 THEN 1 ELSE 0 END 
    FROM #PHOTOCOUNT
) RANGES
GROUP BY
    [Period Start]
  , [Period End] 
;

DROP TABLE #PERIODS;
DROP TABLE #PHOTOCOUNT;