Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何根据列的变化值对记录进行分组/排序?_Sql_Sql Server_Tsql - Fatal编程技术网

Sql 如何根据列的变化值对记录进行分组/排序?

Sql 如何根据列的变化值对记录进行分组/排序?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我按Id订购了下表,年份描述 Id Year Valid 1 2011 1 1 2010 1 1 2009 0 1 2002 1 4 2013 1 4 2012 1 4 2011 1 etc. 身份证有效年份 1 2011 1 1 2010 1 1 2009 0 1 2002 1 4 2013 1 4 2012 1 4 2011 1 等 我想要的是一

我按Id订购了下表,年份描述

Id Year Valid 1 2011 1 1 2010 1 1 2009 0 1 2002 1 4 2013 1 4 2012 1 4 2011 1 etc. 身份证有效年份 1 2011 1 1 2010 1 1 2009 0 1 2002 1 4 2013 1 4 2012 1 4 2011 1 等 我想要的是一个额外的排名字段,如:

Id Year Valid Rank 1 2011 1 1 1 2010 1 1 1 2009 0 2 1 2002 1 3 4 2013 1 1 4 2012 1 1 4 2011 1 1 etc. 身份证年份有效职级 1 2011 1 1 1 2010 1 1 1 2009 0 2 1 2002 1 3 4 2013 1 1 4 2012 1 1 4 2011 1 1 等 基本上每个Id为有效字段中的每个更改交替排列。通过这种方式,我可以查询rank=1字段,以便在第一个Valid=0之前,每个Id都有所有Valid=1记录。或者,是否有一种更简单的方法来选择与特定条件匹配的前两条记录(对于Id=1,仅前两条记录)。我已经玩过ROW_NUMBER()、RANK()和PARTITION BY,但似乎无法让它正常工作。必须避免嵌套查询,因为实际查询是针对大型数据库运行的

有人有什么想法吗

谢谢大家,干杯,
Nyquist

如果您使用的是SQL 2012,则可以使用
lag

select id, year, valid,
    case when ch = 0 then 1 else lag(ch,1,0) over (order by id, year desc) + 2 end rank
from
    (
        select 
            * ,
            abs(valid - lag(valid,1,1) over (order by id, year desc)) as ch
        from YourTable
    ) t 

是的,使用左连接我们可以做到这一点。 请参阅下面的代码和结果

第一个图像是插入的实际数据,第二个图像是预期结果

DECLARE @t TABLE
(
    id      INT
    ,_YEAR  INT
    ,valid  TINYINT
)
INSERT INTO @t( id, [_YEAR], valid )
            SELECT 1,2011,1
UNION ALL   SELECT 1,2010,1
UNION ALL   SELECT 1,2009,0
UNION ALL   SELECT 1,2002,1
UNION ALL   SELECT 4,2013,1
UNION ALL   SELECT 4,2012,1
UNION ALL   SELECT 4,2011,1
UNION ALL   SELECT 5,2013,0
UNION ALL   SELECT 5,2011,1
UNION ALL   SELECT 5,2010,1
UNION ALL   SELECT 6,2010,1
UNION ALL   SELECT 6,2011,0
UNION ALL   SELECT 6,2014,1


SELECT  q1.*
FROM @t q1
LEFT JOIN 
(
    SELECT id,MAX(_YEAR) ZeroYear
    FROM @t
    WHERE valid = 0
    GROUP BY id
)q2
    ON q1.id=q2.id
WHERE 
(q2.ID IS NULL)
OR
(q2.id IS NOT NULL AND q1.id IS NOT NULL AND q1.id=q2.id AND q1.[_YEAR] > q2.ZeroYear)

编辑-1:
在上面对ZeroYear列的查询中,以前我使用了MIN(_YEAR),但正如您在“Andriy M”的注释中看到的那样,MIN right函数的值是MAX。

这与此类似,但不使用联接,而是使用窗口聚合函数:

WITH derived AS (
  SELECT
    Id,
    Year,
    Valid,
    LatestInvalidYear = ISNULL(
      MAX(CASE Valid WHEN 0 THEN Year END) OVER (PARTITION BY Id),
      0
    )
  FROM atable
)
SELECT
  Id,
  Year,
  Valid
FROM derived
WHERE Year > LatestInvalidYear
;
基本上,窗口最大值计算每个
Id
的最新
Valid=0
年。如果找不到这样的年份,MAX将生成NULL,并用ISNULL替换为0。因此,对于您的示例,
派生的
集将返回如下:

Id   Year   Valid   LatestInvalidYear
--   ----   -----   -----------------
1    2011   1       2009
1    2010   1       2009
1    2009   0       2009
1    2002   1       2009
4    2013   1       0
4    2012   1       0
4    2011   1       0

显然,您现在可以轻松地应用过滤器
Year>latestinvalidear
来获取所需的行,这就是主选择的功能。

您能否澄清“排名”列是如何工作的,以及您将如何查询您正在进行的此选择。工作起来很有魅力!谢谢虽然OP说,“每个Id直到第一个有效值=0”,但他们的意思是按照
Year DESC
的顺序,因此您可能应该使用
MAX(\u Year)
而不是
MIN(\u Year)
。另外,主SELECT的WHERE子句中的一些谓词是冗余的,但通常情况下,这样做很好。您可以在WHERE子句中指定冗余条件吗。我将非常感谢您抽出时间。学习高效的编码总是件好事。是的。我查看是否有多个零查询年份出现,应选择最大值而不是最小值。我已更新查询。