查询技巧 - 一种非透视

时间:2014-02-21 14:05:52

标签: sql-server sql-server-2008 tsql sql-server-2008-r2 sql-server-2012

我有下表

SnapShotDay OperationalUnitNumber   IsOpen      StatusDate
1-01-2014       001                   1         1-01-2014
2-01-2014       NULL                NULL           NULL
3-01-2014       001                   0         3-01-2014
4-01-2014       NULL                NULL           NULL
5-01-2014       001                   1         5-01-2014

我使用SELECT构造获得了这个,但我现在需要做的是通过从之前的第一个非空行中获取值来填充“NULL”ed行。后者会给出:

SnapShotDay OperationalUnitNumber   IsOpen      StatusDate
1-01-2014       001                   1         1-01-2014
2-01-2014       001                   1         1-01-2014
3-01-2014       001                   0         3-01-2014
4-01-2014       001                   0         3-01-2014
5-01-2014       001                   1         5-01-2014

在功能性词语中:我有一些事件记录,可以为我提供一个关于操作单位日期的事件;事件是:IsOpen或IsClosed。根据日期将这些事件链接在一起会产生一种范围。我需要的是为这些范围生成每日记录(目标是事实表)。 我试图在纯SQL查询(没有存储过程)中实现这一点。

你能想到一个技巧吗?

4 个答案:

答案 0 :(得分:1)

Declare @t table(
    SnapShotDay date, 
    OperationalUnitNumber int,   
    IsOpen bit, 
    StatusDate date
)

insert into @t
select '1-01-2014',       001 , 1 , '1-01-2014' union all
select '2-01-2014',       NULL, NULL, NULL union all
select '3-01-2014',       001 , 0 ,'3-01-2014' union all
select '4-01-2014',       NULL,NULL,NULL union all
select '5-01-2014',       001 ,1,'5-01-2014'
;
with CTE as
(
    select *,row_number()over( order by (select 0))rn from @t
)
select *,
case when a.isopen is null then (
        select IsOpen from cte where rn=a.rn-1
    ) else a.isopen end 
from cte a 
  

好吧,我得到它再创建一个cte1,

,cte1 as
(
select top 1 rn ,IsOpen from cte where IsOpen is not null order by rn desc
)
--select * from Statuses
select *,
    case 
        when a.rn<=(select b.rn from cte1 b)  and a.IsOpen is null then 
            (
                select 
                    a1.IsOpen 
                from 
                    cte a1

                where 
                    a1.rn=a.rn-1 
            ) 
         when a.rn>=(select b.rn from cte1 b) and  a.IsOpen is null then
         (select IsOpen from cte1)
        else 
        a.isopen 
    end 
from 
    cte a

答案 1 :(得分:0)

试试这个。在主查询中,我们正在寻找前一个日期,而不是空值。然后只需使用此LastDate加入此表。

WITH T1 AS 
(
  SELECT *, (SELECT MAX(SnapShotDay) 
                    FROM T 
                    WHERE SnapShotDay<=TMain.SnapShotDay
                          AND OPERATIONALUNITNUMBER IS NOT NULL) 
             as LastDate
  FROM T as TMain
 )

  SELECT T1.SnapShotDay,
         T.OperationalUnitNumber,
         T.IsOpen,
         T.StatusDate 
    FROM T1
    JOIN T ON T1.LastDate=T.SnapShotDay 

SQLFiddle demo

答案 2 :(得分:0)

SELECT 
    t1.SnapShotDay,
    CASE WHEN t1.OperationalUnitNumber IS NOT NUll 
        THEN t1.OperationalUnitNumber 
        ELSE  (SELECT TOP 1 t2.OperationalUnitNumber FROM  YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.OperationalUnitNumber IS NOT NULL ORDER BY SnapShotDay DESC)
    END AS OperationalUnitNumber,
    CASE WHEN t1.IsOpen IS NOT NUll 
        THEN t1.IsOpen 
        ELSE  (SELECT TOP 1 t2.IsOpen FROM  YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.IsOpen IS NOT NULL ORDER BY SnapShotDay DESC)
    END AS IsOpen,  
    CASE WHEN t1.StatusDate IS NOT NUll 
        THEN t1.StatusDate 
        ELSE  (SELECT TOP 1 t2.StatusDate FROM  YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.StatusDate IS NOT NULL ORDER BY SnapShotDay DESC)
    END AS StatusDate
FROM YourTable t1 

答案 3 :(得分:0)

你问'普通的sql',这是一个使用SQL的测试尝试,带有注释,提供了所需的答案。

我在windows xp上使用'sqlite'和'mysql'测试了代码。它是纯SQL,应该可以在任何地方使用。

SQL是关于'sets'并将它们组合在一起并对结果进行排序。

这个问题似乎是两个独立的集合:

1)有阅读的“拍摄日”。

2)没有读数的“拍摄日”。

我添加了额外的列,以便我们可以轻松查看值的来源。

让我们先处理简单的设置:

这是“提供的”读数集。

SELECT dss.SnapShotDay theDay, 
       'supplied'  readingExists,       
       dss.OperationalUnitNumber,
       dss.IsOpen,       
       dss.StatusDate       
FROM dailysnapshot dss
WHERE dss.OperationalUnitNumber IS NOT NULL

结果:

theDay      readingExists   OperationalUnitNumber   IsOpen  StatusDate
2014-01-01  supplied        001                     1       2014-01-01
2014-01-03  supplied        001                     0       2014-01-03
2014-01-05  supplied        001                     1       2014-01-05

现在让我们来处理那些“缺少读数的日子”。我们需要获得“最近一天的读数最接近当天的读数”,并假设“当前”缺失日之前的“最近一天”具有相同的值。

听起来很复杂,但事实并非如此。它问: 没有阅读的前一天 - 让我获得最接近的早期阅读日期,我将使用这些阅读材料。

以下是查询:

SELECT emptyDSS.SnapShotDay,  
       'missing'  readingExists,       
       maxPrevDSS.OperationalUnitNumber,
       maxPrevDSS.IsOpen,       
       maxPrevDSS.StatusDate       
FROM dailysnapshot emptyDSS
  INNER JOIN dailysnapshot maxPrevDSS ON maxPrevDSS.SnapShotDay = 
                                                 (SELECT MAX(dss.SnapShotDay)
                                                  FROM   dailysnapshot dss
                                                  WHERE  dss.SnapShotDay < emptyDSS.SnapShotDay
                                                  AND    dss.OperationalUnitNumber IS NOT NULL)                                                  
WHERE emptyDSS.OperationalUnitNumber IS NULL

结果:

SnapShotDay readingExists   OperationalUnitNumber   IsOpen  StatusDate
2014-01-02  missing         001                     1       2014-01-01
2014-01-04  missing         001                     0       2014-01-03

这不是效率!它是关于使用最容易理解的SQL代码获取正确的“结果集”。我假设数据库引擎将优化查询。如果需要,可以稍后“调整”查询。

我们现在需要将两个查询组合在一起,并按照我们需要的方式对结果进行排序。

组合SQL查询结果的标准方法是使用set运算符(union,intersection,minus)。

我们在结果集上使用'union'和'order by'。

这给出了最终查询:

SELECT dss.SnapShotDay theDay, 
       'supplied'  readingExists,       
       dss.OperationalUnitNumber,
       dss.IsOpen,       
       dss.StatusDate       
FROM dailysnapshot dss
WHERE `OperationalUnitNumber` IS NOT NULL
UNION
SELECT emptyDSS.SnapShotDay theDay,  
       'missing'  readingExists,       
       maxPrevDSS.OperationalUnitNumber,
       maxPrevDSS.IsOpen,       
       maxPrevDSS.StatusDate       
FROM dailysnapshot emptyDSS
  INNER JOIN dailysnapshot maxPrevDSS ON  maxPrevDSS.SnapShotDay = 
                                                 (SELECT MAX(dss.SnapShotDay)
                                                  FROM   dailysnapshot dss
                                                  WHERE  dss.SnapShotDay < emptyDSS.SnapShotDay
                                                  AND    dss.OperationalUnitNumber IS NOT NULL)
WHERE emptyDSS.OperationalUnitNumber IS NULL
ORDER BY theDay ASC

结果:

theDay      readingExists   dss.OperationalUnitNumber  dss.IsOpen  dss.StatusDate
2014-01-01  supplied        001                        1           2014-01-01
2014-01-02  missing         001                        1           2014-01-01
2014-01-03  supplied        001                        0           2014-01-03
2014-01-04  missing         001                        0           2014-01-03
2014-01-05  supplied        001                        1           2014-01-05

我喜欢这样做。

它应该适用于大多数SQL引擎。