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

Is there a better way to retrieve these data?

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

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');

我的印象是您正试图从 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
;

我实际上建议使用一种条件聚合的变体,它可能更简洁一些,然后我想指出 iamdave 获取 MostRecent 记录的技术会产生超过 1 个结果的细微差别,如果 ProductNRMAX(date) 中有多个记录。我意识到在您的数据集中可能不太可能,其他阅读 post 的人可能会这样。出于这个原因,如果您确实想要领带,我建议使用 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

如果你确实想要领带,它就会变成 true 条件聚合,如下所示:

;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 作为潜在的替代方案。