SQL选择查询 - 根据列的最新值选择表中的行

时间:2015-10-16 02:56:44

标签: sql sql-server sql-server-2008

我有这个表格样本

enter image description here

我需要根据产生此类输出的最新日期选择最新的区域值

enter image description here

4 个答案:

答案 0 :(得分:2)

使用Gaps and Islands解决方案:

SQL Fiddle

WITH CteIslands AS(
    SELECT *,
        grp = DATEADD(DAY, -ROW_NUMBER() OVER(PARTITION BY tenant, area ORDER BY date), date)
    FROM yourTable
),
Cte AS(
    SELECT *,
        rnk = RANK() OVER(PARTITION BY tenant ORDER BY grp DESC, area)
    FROM CteIslands
)
SELECT tenant, area, date, sales
FROM Cte WHERE rnk  = 1

答案 1 :(得分:1)

使用Windowing Functionsthe APPLY operator的解决方案可能更简单,执行效率更高,但有些人很难使用这些解决方案,我怀疑最好的结果会使用LAG / LEAD ,这在Sql Server 2008中不可用。因此,我提供了一个纯粹基于JOIN和GROUP BY的解决方案。

首先,您需要知道每个租户的最新日期:

SELECT tenant, MAX(date) date
FROM sample s1
GROUP BY s1.tenant

您可以使用它来查找该行的area值:

SELECT tenant, area, date
FROM sample s
INNER JOIN (
    SELECT tenant, MAX(date) date
    FROM sample s1
    GROUP BY s1.tenant
) t on t.tenant = s.tenant and t.date = s.date

现在您可以使用它来查找不具有相同区域的最新日期:

SELECT s3.tenant, MAX(date) date
FROM sample s3
INNER JOIN (
    SELECT tenant, area, date
    FROM sample s2
    INNER JOIN (
        SELECT tenant, MAX(date) date
        FROM sample s1
        GROUP BY s1.tenant
    ) j1 on j1.tenant = s.tenant and j1.date = s.date
) j2 on j2.tenant = s3.tenant and j2.area <> s3.area
GROUP BY s3.tenant

现在,您可以使用此日期从每个租户中选择日期更大的所有记录:

SELECT s4.*
FROM sample s4
INNER JOIN (
    SELECT s3.tenant, MAX(date) date
    FROM sample s3
    INNER JOIN (
        SELECT tenant, area
        FROM sample s2
        INNER JOIN (
            SELECT tenant, MAX(date) date
            FROM sample s1
            GROUP BY s1.tenant
        ) j1 on j1.tenant = s.tenant and j1.date = s.date
    ) j2 on j2.tenant = s3.tenant and j2.area <> s3.area
    GROUP BY s3.tenant
) j3 on j3.tenant = s4.tenant and s4.date > j4.date

值得注意的是,我可以重新使用嵌套连接中的表别名,但我经常发现在编写查询时如果我保持它们的唯一性,就更容易跟踪事物。您还可以通过将嵌套查询移到CTE来提高可读性,这有助于保留我呈现的逻辑流程:

-- Most recent date for each tenant
WITH LatestDates AS
(
    SELECT tenant, MAX(date) date
    FROM sample
    GROUP BY tenant
),
-- the area for each tenant on that date
LatestAreas AS
(
    SELECT tenant, area
    FROM sample s
    INNER JOIN LatestDates l on l.tenant = s.tenant and l.date = s.date
),
-- the most recent date for each tenant where the area is different
LatestDateWithChangedArea AS
(
    SELECT s.tenant, MAX(date) date
    FROM sample s
    INNER JOIN LatestAreas l on l.tenant = s.tenant and l.area <> s.area
    GROUP BY s.tenant
)
-- all records for each tenant where the date is greater than that
SELECT s.*
FROM sample s
INNER JOIN LatestDateWithChangedArea l ON s.tenant = l.tenat and s.date > l.date

正如我之前提到的,我们可以使用APPLY运算符来进一步简化:

WITH LatestDates AS
(
    SELECT tenant, MAX(date) date
    FROM sample
    GROUP BY tenant
)
SELECT s3.*
FROM LatestDates l
INNER JOIN sample s ON s.tenant = l.tenant and s.date = l.date
OUTER APPLY (
  SELECT TOP 1 tenant, date 
  FROM sample s2
  WHERE s2.tenant = s.tenant and s2.area<>s.area
  ORDER BY s2.tenant, s2.date desc
) a
INNER JOIN sample s3 ON s3.tenant = a.tenant and s3.date > a.date
  

<强> SQL Fiddle

(感谢其他海报,为我节省了一些时间将模式放在一起。)

答案 2 :(得分:1)

通过Felix修复解决方案。我认为你不应该在第一个CTE中按area进行分区。您应该在第二个CTE中按area进行分区,而不是按它排序。

SQL Fiddle

WITH 
CTE1
AS
(
    SELECT *,
        ROW_NUMBER() OVER(PARTITION BY tenant ORDER BY date desc) AS rn
    FROM yourTable
)
,CTE2
AS
(
  SELECT
    *
    ,rn - ROW_NUMBER() OVER (PARTITION BY tenant, area ORDER BY rn) AS rnk
  FROM CTE1
)
SELECT
  tenant
  ,area
  ,date
  ,sales
FROM CTE2
WHERE rnk = 0
ORDER BY tenant, date desc

答案 3 :(得分:1)

可以尝试这个

//get latest date record for the tenant
WITH LatestData AS
    (SELECT tenant, area, date 
     FROM tenant_table as a
     WHERE DATE = (SELECT MAX(date) FROM tenant_table as b
                    WHERE a.tenant = b.tenant)
    ),
//get latest date record for the tenant with area not the latest area
     LatestDateWithAreaChanged AS
    (SELECT tenant, max(date)
     FROM tenant_table as c INNER JOIN LatestData as D 
                            ON c.tenant = D.tenant and c.area  d.area
     GROUP BY tenant)
//get all data where date is after the last area changed     
SELECT X.* FROM tenant_table as X
INNER JOIN LatestDateWithAreaChanged as Y
ON X.tenat = Y.tenant AND X.date > Y.date