SQL:固定行和行号计算

时间:2018-12-14 04:03:15

标签: sql sorting sql-server-2012 row-number

我们需要使用以下规则为所有行分配行号

  • 如果固定的行应该具有相同的行号
  • 否则按GMD排序

示例:

>>>train.shape
(2,3)
>>>train.shape[0]              # will display number of rows
2
>>>train.shape[1]              # will display number of columns
3

应输出

 ID  GMD   IsPinned 

 1   2.5    0        
 2    0     1        
 3    2     0        
 4    4     1        
 5    3     0       

请注意,Id的2和4的行号保持不变,因为它们分别固定为2和4的值,即使GMD不在任何顺序 使用GMD desc对ID的其余1、3和5行编号进行排序

我尝试使用RowNumber SQL 2012,但是它从其位置推送固定的项目

4 个答案:

答案 0 :(得分:1)

这是一种基于集合的方法来解决此问题。请注意,如果您的数据库中已经有一个Numbers表,则不需要前一个CTE

declare @t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into @t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)

;With Numbers as (
    select ROW_NUMBER() OVER (ORDER BY ID) n from @t
), NumbersWithout as (
    select
        n,
        ROW_NUMBER() OVER (ORDER BY n) as rn
    from
        Numbers
    where n not in (select ID from @t where IsPinned=1)
), DataWithout as (
    select
        *,
        ROW_NUMBER() OVER (ORDER BY GMD desc) as rn
    from
        @t
    where
        IsPinned = 0
)
select
    t.*,
    COALESCE(nw.n,t.ID) as RowNo
from
    @t t
        left join
    DataWithout dw
        inner join
    NumbersWithout nw
        on
            dw.rn = nw.rn
        on
            dw.ID = t.ID
order by COALESCE(nw.n,t.ID)

希望我的名字可以清楚地说明我们在做什么。当您可能期望使用SELECT表达式时,通过使用COALESCE获得最终的RowNo,我对最后的CASE有点厚颜无耻。但这是可行的,因为DataWithout CTE的内容被定义为仅存在于未固定的项目中,从而导致最终的LEFT JOIN失败。

结果:

ID          GMD                                     IsPinned RowNo
----------- --------------------------------------- -------- --------------------
5           3.00                                    0        1
2           0.00                                    1        2
1           2.50                                    0        3
4           4.00                                    1        4
3           2.00                                    0        5

第二种效果可能更好(但从不假设,请始终进行测试):

declare @t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into @t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)

;With Numbers as (
    select ROW_NUMBER() OVER (ORDER BY ID) n from @t
), NumbersWithout as (
    select
        n,
        ROW_NUMBER() OVER (ORDER BY n) as rn
    from
        Numbers
    where n not in (select ID from @t where IsPinned=1)
), DataPartitioned as (
    select
        *,
        ROW_NUMBER() OVER (PARTITION BY IsPinned ORDER BY GMD desc) as rn
    from
        @t
)
select
    dp.ID,dp.GMD,dp.IsPinned,
    CASE WHEN IsPinned = 1 THEN ID ELSE nw.n END as RowNo
from
    DataPartitioned dp
        left join
    NumbersWithout nw
        on
            dp.rn = nw.rn
order by RowNo

在第三个CTE中,通过引入PARTITION BY并删除WHERE子句,我们确保已拥有所有数据行,因此在最终版本中无需重新连接至原始表导致出现这种变体。

答案 1 :(得分:0)

这将起作用:

CREATE TABLE Table1
    ("ID" int, "GMD" number, "IsPinned" int)
;

INSERT ALL 
    INTO Table1 ("ID", "GMD", "IsPinned")
         VALUES (1, 2.5, 0)
    INTO Table1 ("ID", "GMD", "IsPinned")
         VALUES (2, 0, 1)
    INTO Table1 ("ID", "GMD", "IsPinned")
         VALUES (3, 2, 0)
    INTO Table1 ("ID", "GMD", "IsPinned")
         VALUES (4, 4, 1)
    INTO Table1 ("ID", "GMD", "IsPinned")
         VALUES (5, 3, 0)
SELECT * FROM dual
;


select * from (select  "ID","GMD","IsPinned",rank from(select m.*,rank()over(order by 
"ID" asc) rank from Table1 m where "IsPinned"=1)
union
(select "ID","GMD","IsPinned",rank from (select t.*,rank() over(order by "GMD" 
desc)-1 rank from (SELECT * FROM Table1)t)
where "IsPinned"=0) order by "GMD" desc) order by rank ,GMD;

输出:

   2    0   1   1
   5    3   0   1
   1    2.5 0   2
   4    4   1   2
   3    2   0   3

答案 2 :(得分:0)

您可以尝试查询吗

CREATE TABLE Table1
    (ID int, GMD numeric (18,2), IsPinned int);


INSERT INTO Table1 (ID,GMD, IsPinned)
         VALUES (1, 2.5, 0), 
                (2, 0, 1),
                (3, 2, 0),
                (4, 4, 1),
                (5, 3, 0)





   select *, row_number () over(partition by IsPinned order by (case when IsPinned =0 then GMD else id end) ) [CustOrder] from Table1

Result

答案 3 :(得分:0)

这花了更长的时间,而我认为,问题是row_number可以解决查询。我们需要先按id区分row_numbers,然后才能应用while循环或游标或任何迭代,在本例中,我们将仅使用while循环。

dbo.test(您可以用表名替换test)

1   2.5 False
2   0   True
3   3   False
4   4   True
6   2   False

这是我为达到目标而编写的查询,如果您有任何困难要告诉我,我会在每个操作下添加注释。

查询

--user data table 
DECLARE @userData TABLE 
  ( 
     id        INT NOT NULL, 
     gmd       FLOAT NOT NULL, 
     ispinned  BIT NOT NULL, 
     rownumber INT NOT NULL 
  ); 
--final result table 
DECLARE @finalResult TABLE 
  ( 
     id           INT NOT NULL, 
     gmd          FLOAT NOT NULL, 
     ispinned     BIT NOT NULL, 
     newrownumber INT NOT NULL 
  ); 

--inserting to uer data table from the table test 
INSERT INTO @userData 
SELECT t.*, 
       Row_number() 
         OVER ( 
           ORDER BY t.id ASC) AS RowNumber 
FROM   test t 

--creating new table for ids of not pinned 
CREATE TABLE #ids 
  ( 
     rn  INT, 
     id  INT, 
     gmd FLOAT 
  ) 

-- inserting into temp table named and adding gmd by desc
INSERT INTO #ids 
            (rn, 
             id, 
             gmd) 
SELECT DISTINCT Row_number() 
                  OVER( 
                    ORDER BY gmd DESC) AS rn, 
                id, 
                gmd 
FROM   @userData 
WHERE  ispinned = 0 

--declaring the variable to loop through all the no pinned items
DECLARE @id INT 
DECLARE @totalrows INT = (SELECT Count(*) 
   FROM   #ids) 
DECLARE @currentrow INT = 1 
DECLARE @assigningNumber INT = 1 

--inerting pinned items first
INSERT INTO @finalResult 
SELECT ud.id, 
       ud.gmd, 
       ud.ispinned, 
       ud.rownumber 
FROM   @userData ud 
WHERE  ispinned = 1 

--looping through all the rows till all non-pinned items finished
WHILE @currentrow <= @totalrows 
  BEGIN 
        --skipping pinned numers for the rows
      WHILE EXISTS(SELECT 1 
                   FROM   @finalResult 
                   WHERE  newrownumber = @assigningNumber 
                          AND ispinned = 1) 
        BEGIN 
            SET @assigningNumber = @assigningNumber + 1 
        END 

      --getting row by the number
      SET @id = (SELECT id 
                 FROM   #ids 
                 WHERE  rn = @currentrow) 

      --inserting the non-pinned item with new row number into the final result
      INSERT INTO @finalResult 
      SELECT ud.id, 
             ud.gmd, 
             ud.ispinned, 
             @assigningNumber 
      FROM   @userData ud 
      WHERE  id = @id       

      --going to next row
      SET @currentrow = @currentrow + 1 
      SET @assigningNumber = @assigningNumber + 1 
  END 

--getting final result
SELECT * 
FROM   @finalResult 
ORDER  BY newrownumber ASC 

--dropping table
DROP TABLE #ids 

输出:

enter image description here