如何在没有百万和/或的情况下缩短这个SQL查询?

时间:2014-08-17 21:13:41

标签: sql sql-server

我有一个包含13列的表(一个用于行ID#,另外12个表示一年中每个月的一个)。每列只包含数字,我想编写几个查询来检查每一行的特定条件,并返回匹配的任何行的ID。

到目前为止,我一直很好地编写所有基本的SELECT查询,但现在我在编写SELECT查询时遇到了一些问题,无法一次检查多个条件,而且每次都没有编写一百万行代码。

我正在编写的当前查询需要检查每对连续月份(例如1月/ 2月,2月/ 3月等)以查看它们之间的差异有多大,它需要返回任意两行的行连续几个月有一个&gt;它们与剩余对之间的20%差异全部<1。 10%的差异。

例如,如果1月是1000而2月是1300,那就是30%的差异,所以这是1手。然后,如果说4月是1500,5月是2100,那就是40%的差异,所以有第2批。然后只要每隔一对(2月/ 3月,3月/ 4月,......,11月/ 12月)都是&lt;每个都有10%的差异,然后需要返回该行。

不幸的是,我能让它工作的唯一方法是手动检查每一种可能性(哪种方法有效),但对于编写类似的查询并不是非常可重复使用。

这是我到目前为止的缩写版本:

SELECT pkID
FROM dbo.tblMonthData
WHERE

((colFeb > colJan * 1.2 AND colMar > colFeb * 1.2) AND (colApr < colMar * 1.1 AND colMay < colApr * 1.1 AND colJun < colMay * 1.1 AND colJul < colJun * 1.1 AND colAug < colJul * 1.1 AND colSep < colAug * 1.1 AND colOct < colSep * 1.1 AND colNov < colOct * 1.1 AND colDec < colNov * 1.1))

OR ((colFeb > colJan * 1.2 AND colApr > colMar * 1.2) AND (colMar < colFeb * 1.1 AND colMay < colApr * 1.1 AND colJun < colMay * 1.1 AND colJul < colJun * 1.1 AND colAug < colJul * 1.1 AND colSep < colAug * 1.1 AND colOct < colSep * 1.1 AND colNov < colOct * 1.1 AND colDec < colNov * 1.1))

OR ((colFeb > colJan * 1.2 AND colMay > colApr * 1.2) AND (colMar < colFeb * 1.1 AND colApr < colMar * 1.1 AND colJun < colMay * 1.1 AND colJul < colJun * 1.1 AND colAug < colJul * 1.1 AND colSep < colAug * 1.1 AND colOct < colSep * 1.1 AND colNov < colOct * 1.1 AND colDec < colNov * 1.1))

...

OR ((colNov > colOct * 1.2 AND colDec > colNov * 1.2) AND (colFeb < colJan * 1.1 AND colMar < colFeb * 1.1 AND colApr < colMar * 1.1 AND colMay < colApr * 1.1 AND colJun < colMay * 1.1 AND colJul < colJun * 1.1 AND colAug < colJul * 1.1 AND colSep < colAug * 1.1 AND colOct < colSep * 1.1))

总共有大约55行OR语句来检查每种可能的组合。如果我然后去查询类似的东西(例如,返回所有行ID#,其中2对大于50%,4对大于10%),这涉及从头开始编写另一个长查询,检查每个其他可能的组合。

所以我想知道如何在更短的版本中重写这个版本,这个版本对于类似的查询也可以更加重复使用?

3 个答案:

答案 0 :(得分:4)

取消激活表格以执行比较,然后根据需要转回原始格式。:

with 
unpvt as (
    select    
         YearNo     
        ,case MonthNo when  1 then colJan
                      when  2 then colFeb
                      when  3 then colMar
                      when  4 then colApr
                      when  5 then colMay
                      when  6 then colJun
                      when  7 then colJul
                      when  8 then colAug
                      when  9 then colSep
                      when 10 then colOct
                      when 11 then colNov
                      when 12 then colDec 
                              else 0
         end as Value
        ,YearNo * 12 + MonthNo - 1 as PeriodNo
        /* other columns */
--    from dbo.tblMonthData
    from tblMonthData
    cross join ( values
        (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
    )months(MonthNo)
)
select 
    this.*,
    this.Value - isnull(prev.Value,0) as Delta
from unpvt          this
left join unpvt     prev
   on prev.PeriodNo = this.PeriodNo - 1
;

等。根据您的SQL Server版本,您可以访问UNPIVOT clause以进一步压缩源。

如果您有static NUMBERS table,那么可以使用前十二行代替CROSS JOIN中的VALUE初始化。

答案 1 :(得分:1)

因此,作为@ Pieter的答案的替代答案,并说明一个更简单的数据结构如何使您的任务更容易,我建议如下:

create view tblEasy as (
select pkID, 1 as colMonth, colJan as colValue from tblMonthData
UNION
select pkID, 2 as colMonth, colFeb as colValue from tblMonthData
UNION
select pkID, 3 as colMonth, colMar as colValue from tblMonthData
UNION
select pkID, 4 as colMonth, colApr as colValue from tblMonthData
UNION
select pkID, 5 as colMonth, colMay as colValue from tblMonthData
UNION
select pkID, 6 as colMonth, colJun as colValue from tblMonthData
UNION
select pkID, 7 as colMonth, colJul as colValue from tblMonthData
UNION
select pkID, 8 as colMonth, colAug as colValue from tblMonthData
UNION
select pkID, 9 as colMonth, colSep as colValue from tblMonthData
UNION
select pkID, 10 as colMonth, colOct as colValue from tblMonthData
UNION
select pkID, 11 as colMonth, colNov as colValue from tblMonthData
UNION
select pkID, 12 as colMonth, colDec as colValue from tblMonthData
);

这使视图看起来像我最初构建表格的方式。然后,通过将colMonth上的值与colMonth + 1上的值进行比较,可以轻松创建对。

我做了一个小提琴来说明如何在一个视图中进行比较,然后查询本身,如果相当明显。

http://sqlfiddle.com/#!3/600f6/4

请注意,由于初始表结构,性能不佳。


<强>更新 由于这被接受作为答案,我将从sqlfiddle中嵌入额外的细节。

预先计算连续月份之间差异的额外视图:

create view tblPairs as (
  select t1.pkId , t1.colMonth as colStart, (t2.colValue * 100 / t1.colValue) as colPercentage
  from tblEasy as t1
  inner join tblEasy as t2 
  on t1.pkId = t2.pkId and t1.colMonth = t2.colMonth - 1);

查询以查找2个月的增长率超过20%,其他9个月的增幅低于10%:

select distinct pkid
from tblPairs as t1
where 2 = (
    select count(*)
    from tblPairs as t2
    where t2.pkid = t1.pkid
    and colPercentage >= 120)
and 9 = (
    select count(*)
    from tblPairs as t2
    where t2.pkid = t1.pkid
    and colPercentage <= 110)
;

答案 2 :(得分:1)

您可以使用table valued constructor创建具有两个连续月份值的派生表,在交叉申请中逐行取消数据。同样在交叉申请中,您可以使用casesum进行计算并计算符合特定条件的行。

您的查询可能看起来像这样。

select MD.pkID
from dbo.tblMonthData as MD
  cross apply (
              select sum(case when P.Mon1 * 1.2 < P.Mon2 then 1 end),
                     sum(case when P.Mon1 * 1.1 > P.Mon2 then 1 end)
              from (values(MD.colJan, MD.colFeb),
                          (MD.colFeb, MD.colMar),
                          (MD.colMar, MD.colApr),
                          (MD.colApr, MD.colMay),
                          (MD.colMay, MD.colJun),
                          (MD.colJun, MD.colJul),
                          (MD.colJul, MD.colAug),
                          (MD.colAug, MD.colSep),
                          (MD.colSep, MD.colOct),
                          (MD.colOct, MD.colNov),
                          (MD.colNov, MD.colDec)) as P(Mon1, Mon2)
              ) as C(MoreThan20, LessThan10)
where C.MoreThan20 = 2 and
      C.LessThan10 = 9