合并具有相同核心语法的多个查询

时间:2014-10-09 08:04:05

标签: sql sql-server-2012 query-optimization

对于模糊的标题感到抱歉!

我正在构建一个Web过滤器,用户可以使用MS-SQL 2012单击选项并过滤结果

我遇到的问题是我在为构建过滤器而选择的每个选项上运行4个查询,并为结果运行1个查询。

忽略如何对其进行最佳编码,以便每次都不必重新加载过滤器查询,我需要帮助我如何合并产生过滤器选项的4个查询并计入1。

语法的核心是相同的,其中大多数逻辑基于过滤的选择提取结果。但是,我仍然需要生成带有计数的过滤器。

        select datePart(yy,p.startDate) year, count(p.personId) itemCount
        from person p
        where p.field1 = something
        and p.field2 = somethingElse...

有没有办法运行核心并生成过滤器列表并计算(见下文)所有的一个而不是单独执行每个过程?

下面是2个过滤器的示例,但是还有其他类似的过滤器,可以根据现有数据生成列表,也可以根据特定日期之前,之间和之后生成列表。

--Filter 1 to get years and counts
with annualList as
(
    select a.year
    from table a
    where a.year > 2000 
)
select al.year, isnull(count,0) itemCount
from annualList al
left join (
        select datePart(yy,p.startDate) year, count(p.personId) itemCount
        from person p
        where p.field1 = something
        and p.field2 = somethingElse...
    ) group by datePart(yy,p.startDate) persons
on al.year = persons.year
order by al.year desc;

--filter 2 to get group stats
select anotherList.groupStatus,  groupStatusCounts.itemCount 
from (
    select 'Child' as groupStatus
    union all
    select 'Adult' as groupStatus
    union all
    select 'Pensioner' as groupStatus
) anotherList
left join (
    SELECT personStatus.groupStatus, count(personStatus.personId) itemCount
    FROM ( select p.personId
           case when (p.age between 1 and 17) then 'Child'
           case when (p.age between 18 and 67) then 'Adult'
           case when (p.age > 65) then 'Pensioner'
           end as groupStatus
           FROM person p
           --and some other syntax to calculate the age...
           where p.field1 = something
           and p.field2 = somethingElse exactly as above query...
         ) personStatus
         GROUP BY personStatus.groupStatus
    ) groupStatusCounts
ON anotherList.groupStatus = groupStatusCounts.groupStatus

例如,使用下面的数据集,从regDate和我使用的组将能够获得2010-2014的年份列表(上面的filter1代码)。

使用dataofbirth我需要计算' groupStatus使用案例。从数据生成数据中可以看出,我没有任何记录可以识别养老金领取者因此我编写过滤器2的方式(参见上面的代码),它会给我提供我需要的过滤器,即使我没有&# 39; t有数据来代表它。

试图在SQL Fiddle上添加代码,不幸的是它昨晚失败了

INSERT INTO personTable
    ([PersonID], [dateOfBirth], [regDate])
VALUES
    (1,  '1979-01-01 00:00:00', '2010-01-01 00:00:00'),
    (2,  '1979-01-01 00:00:00', '2010-01-01 00:00:00'),
    (3,  '1979-01-01 00:00:00', '2011-01-01 00:00:00'),
    (4,  '1979-01-01 00:00:00', '2011-01-01 00:00:00'),
    (5,  '1979-01-01 00:00:00', '2012-01-01 00:00:00'),
    (6,  '1979-01-01 00:00:00', '2012-01-01 00:00:00'),
    (7,  '1984-01-01 00:00:00', '2012-01-01 00:00:00'),
    (8,  '1992-01-01 00:00:00', '2012-01-01 00:00:00'),
    (9,  '2000-01-01 00:00:00', '2013-01-01 00:00:00'),
    (10, '2010-01-01 00:00:00', '2014-01-01 00:00:00')

my example of SQL Fiddle

结果想要结束,我需要根据Fiddle

中显示的结果看起来像这样
filter     | Year   | groupStatus 
-----------+-----------+--------
2010       |  0      |  null
2011       |  2      |  null
2012       |  3      |  null
2013       |  0      |  null
2014       |  3      |  null
child      |  null   |  2
adult      |  null   |  6
pensioner  |  null   |  0

提前感谢你

1 个答案:

答案 0 :(得分:0)

我可能已经大量误解了这个要求,但我怀疑你想要使用GROUPING SETS。作为一个简单示例(忽略您的特定过滤器),想象一下以下简单数据集:

PersonID | GroupStatus | Year 
---------+-------------+--------
    1    |  Adult      |  2013
    2    |  Adult      |  2013
    3    |  Adult      |  2014
    4    |  Adult      |  2014
    5    |  Adult      |  2014
    6    |  Child      |  2012
    7    |  Child      |  2014
    8    |  Pensioner  |  2012
    9    |  Pensioner  |  2013
   10    |  Pensioner  |  2013

从我收集的内容中,您试图从这些数据中获取2个不同的摘要,而不重复查询,例如。

GroupStatus | ItemCount
------------+-----------
    Adult   |     5
    Child   |     2
  Pensioner |     3

    Year    | ItemCount
------------+-----------
    2012    |     2
    2013    |     4
    2014    |     4

您可以使用以下方法在单个数据集中获取此内容:

SELECT  Year, 
        GroupStatus,
        ItemCount = COUNT(*)
FROM    T
GROUP BY GROUPING SETS ((Year), (GroupStatus));

这将产生一个数据集:

YEAR    GROUPSTATUS     ITEMCOUNT
------------------------------------
NULL    Adult           5
NULL    Child           2
NULL    Pensioner       3
2012    NULL            2
2013    NULL            4
2014    NULL            4

如果您还希望包含完整数据,则只需添加包含所有字段的分组集,例如(PersonID, Year, GroupStatus)

<强> Examples on SQL Fiddle

编辑

以下查询给出了您请求的输出:

WITH AllValues AS
(   SELECT  TOP (DATEPART(YEAR, GETDATE()) - 2009)
            Filter = CAST(2009 + ROW_NUMBER() OVER(ORDER BY object_id) AS VARCHAR(15)),
            FilterType = 'Year'
    FROM    sys.all_objects o
    UNION ALL
    SELECT  Filter, 'GroupStatus'
    FROM    (VALUES ('child'), ('Adult'), ('Pensioner')) T (Filter)
)
SELECT  v.Filter,
        [Year] = CASE WHEN v.FilterType = 'Year' THEN ISNULL(data.ItemCount, 0) END,
        GroupStatus = CASE WHEN v.FilterType = 'GroupStatus' THEN ISNULL(data.ItemCount, 0) END
FROM    AllValues AS v
        LEFT JOIN
        (   SELECT  [Year] = DATENAME(YEAR, t.RegDate),
                    Age = a.Name,
                    ItemCount = COUNT(*)
            FROM    T
                    LEFT JOIN 
                    (VALUES 
                        (0, 18, 'Child'), 
                        (18, 65, 'adult'), 
                        (65, 1000000, 'pensioner')
                    ) a (LowerValue, UpperValue, Name)
                        ON a.LowerValue <=  DATEDIFF(HOUR, T.dateofbirth, GETDATE()) / 8766.0
                        AND a.UpperValue >  DATEDIFF(HOUR, T.dateofbirth, GETDATE()) / 8766.0
            WHERE   T.PersonID > 2
            GROUP BY GROUPING SETS ((DATENAME(YEAR, t.RegDate)), (a.Name))
        ) AS data
            ON ISNULL(data.[Year], data.Age) = v.Filter;

<强> Example on SQL Fiddle