有没有更好的方法来检索这些数据?

时间:2016-11-24 12:42:54

标签: sql sql-server full-outer-join

我的下面代码运行正常。它的作用是在移动到新位置时更新每个产品编号

select
 a.loc1 As [Location 1], 
b.loc2 as [Location 2], 
c.loc3 as [Location 3],
d.loc4 as [Location 4]

FROM (select distinct a.ProductNR as Loc1
from LocationsTest a
where a.Date= (select max(Date) from LocationsTest where a.ProductNR = ProductNR)
AND a.Location = 1) as a

FULL OUTER JOIN

(select distinct a.ProductNR as Loc2
from LocationsTest a
where a.Date= (select max(Date) from LocationsTest where a.ProductNR = ProductNR)
AND a.Location = 2) as b
on a.Loc1 = b.Loc2

FULL OUTER JOIN

(select distinct a.ProductNR as Loc3
from LocationsTest a
where a.Date= (select max(Date) from LocationsTest where a.ProductNR = ProductNR)
AND a.Location = 3) as c
ON ISNULL(A.Loc1, b.Loc2) = c.Loc3

FULL OUTER JOIN

(select distinct a.ProductNR as Loc4
from LocationsTest a
where a.Date= (select max(Date) from LocationsTest where a.ProductNR = ProductNR)
AND a.Location = 4) as d
ON ISNULL(b.Loc2, c.Loc3) = d.Loc4

演示其工作原理的一个示例是,您可以看到下面4个位置包含不同产品编号的位置。

----------------------------------------------------------
| Location 1   | Location 2   | Location 3   | Location 4
----------------------------------------------------------
| 1234         |              |              |           |          
| 4567         |              |              |           |
| 8978         |              |              |           |
| 2578         |              |              |           |
----------------------------------------------------------

如果稍后将产品扫描到新位置,它仍会保留在我的历史数据中,就像在位置1中一样,但我的上述查询显示了这一点:

----------------------------------------------------------
| Location 1   | Location 2   | Location 3   | Location 4
----------------------------------------------------------
|              | 1234         |              |           |          
| 4567         |              |              |           |
| 8978         |              |              |           |
| 2578         |              |              |           |
----------------------------------------------------------

它根据上次更新日期检索数据。 问题是我的上述代码看起来很长,尤其是当我计划将来添加更多位置时。 那么有更好的方法吗?

编辑 - 样本数据:

  CREATE TABLE LocationsTest
(
ProductNR varchar (14),
Location int,
Date Datetime,

);

Insert Into LocationsTest (ProductNR, Location, Date)
Values('1234', 1, '2016-11-17 12:30:50.010'), 
      ('4567', 1, '2016-11-17 12:35:50.010'), 
      ('8978', 1, '2016-11-17 12:37:50.010'), 
      ('2578', 1, '2016-11-17 12:50:50.010');

2 个答案:

答案 0 :(得分:1)

我实际上建议使用条件聚合的变体,这可能会更清晰一些,然后我想指出iamdave技术获得MostRecent记录会产生多于1个结果的细微差别。 ProductNR有多个MAX(date)的记录。我意识到你的数据集可能不太可能是其他人阅读帖子。出于这个原因,我建议您使用ROW_NUMBER()来确定所需的记录,如果您确实想要关联,然后使用RANK()DENSE_RANK()

如果你不想要关系我觉得你可以简化,只需要这样:

;WITH cteRowNums AS (
    SELECT
       Location
       ,ProductNR
       ,RowNumber = ROW_NUMBER() OVER (PARTITION BY ProductNR ORDER BY Date DESC)
    FROM
       LocationsTest
)

SELECT DISTINCT
    Location1 = CASE WHEN Location = 1 THEN ProductNR END
    ,Location2 = CASE WHEN Location = 2 THEN ProductNR END
    ,Location3 = CASE WHEN Location = 3 THEN ProductNR END
    ,Location4 = CASE WHEN Location = 4 THEN ProductNR END
FROM       
    cteRowNums
WHERE
     RowNumber = 1

如果你确实需要关系,它就会成为真正的条件聚合,如下所示:

;WITH cteRowNums AS (
    SELECT DISTINCT
       Location
       ,ProductNR
       ,RowNumber = RANK() OVER (PARTITION BY ProductNR ORDER BY Date DESC)
    FROM
       LocationsTest
)

SELECT
    Location1 = MAX(CASE WHEN Location = 1 THEN ProductNR END)
    ,Location2 = MAX(CASE WHEN Location = 2 THEN ProductNR END)
    ,Location3 = MAX(CASE WHEN Location = 3 THEN ProductNR END)
    ,Location4 = MAX(CASE WHEN Location = 4 THEN ProductNR END)
FROM       
    cteRowNums
WHERE
     RowNumber = 1
GROUP BY
    ProductNR

然后使用iamdave的方法,您可以做同样的事情,只使用ROW_NUMBER()RANK()来确定您想要的内容,如下所示:

;WITH cteRowNums AS (
    SELECT
       Location = 'Location' + CAST(Location AS VARCHAR(10))
       ,ProductNR
       ,RowNumber = ROW_NUMBER() OVER (PARTITION BY ProductNR ORDER BY Date DESC)
    FROM
       LocationsTest
)

, cteDesiredRecords AS (
    SELECT
       Location
       ,ProductNR
       ,ProductNR2 = ProductNR
    FROM      
       cteRowNums
    WHERE
       RowNumber = 1
)

SELECT *
FROM
    cteDesiredRecords
    PIVOT (
       MAX(ProductNR)
       FOR Location IN ([Location1],[Location2],[Location3],[Location4])
    ) p

底线是PIVOT是一个很棒的命令,但有时你需要准备好你的记录集来按摩它来做你想做的事情。在那些情况下,您可以将Conditional Aggregation视为潜在替代方案。

答案 1 :(得分:0)

我得到的印象是您正在尝试从SQL代码中找到格式化解决方案,这通常是禁止的。数据的外观应保留在表示层中。

除此之外,下面的代码包括两个例子;第一个是你可能应该将数据返回到你的应用层,第二个是你所要求的格式。但是,当包含新位置时,您需要不断更新PIVOT语句以包含它们:

CREATE TABLE LocationsTest
(
ProductNR varchar (14),
Location int,
Date Datetime

);

Insert Into LocationsTest (ProductNR, Location, Date)
Values('1234', 1, '2016-11-17 12:30:50.010'), 
      ('4567', 1, '2016-11-17 12:35:50.010'), 
      ('8978', 1, '2016-11-17 12:37:50.010'), 
      ('2578', 1, '2016-11-17 12:50:50.010'),
      ('1234', 2, '2016-11-18 12:30:50.010');   -- I have added this row to simulate a Location move.

-- This just drops out the relevant data for use in application level formatting:
with mr
as
(
    select ProductNR
            ,max(Date) as MostRecent
    from LocationsTest
    group by ProductNR
)
select l.ProductNr
        ,l.Location
from LocationsTest l
    inner join mr
        on l.ProductNR = mr.ProductNR
            and l.Date = mr.MostRecent;


-- This actually PIVOTs the data for you, but will need updating for every new location:
with mr
as
(
    select ProductNR
            ,max(Date) as MostRecent
    from LocationsTest
    group by ProductNR
)
select [1] as Location1
        ,[2] as Location2
        ,[3] as Location3
        ,[4] as Location4
from(
    select l.ProductNr
            ,l.ProductNr as ProductNr2  -- This ensures all rows are returned in the PIVOT
            ,l.Location
    from LocationsTest l
        inner join mr
            on l.ProductNR = mr.ProductNR
                and l.Date = mr.MostRecent
) d
pivot
(max(ProductNr) for Location in([1],[2],[3],[4])) pvt
;