簡體   English   中英

SQL查詢返回n個具有空字段或按該字段分組的記錄的前幾條記錄

[英]SQL query to return n top records which have a null field or records grouped by that field

我有一個具有過濾器列的表A

| id |  name  | filter |
| 1  |  joe   |  a     |
| 2  |  anna  |  a     |
| 3  |  mike  | null   |
| 4  |  frank | null   |
| 5  |  sarah |  b     |
| 6  |  jamie |  b     |

假設記錄按ID排序。 具有相同過濾值的記錄應僅計為一個。

TOP(1)應該返回

| id |  name  | filter |
| 1  |  joe   |  a     |
| 2  |  anna  |  a     |

TOP(2)應該返回

| id |  name  | filter |
| 1  |  joe   |  a     |
| 2  |  anna  |  a     |
| 3  |  mike  | null   |

TOP(3)應該返回

| id |  name  | filter |
| 1  |  joe   |  a     |
| 2  |  anna  |  a     |
| 3  |  mike  | null   |
| 4  |  frank | null   |

TOP(4)應該返回

| id |  name  | filter |
| 1  |  joe   |  a     |
| 2  |  anna  |  a     |
| 3  |  mike  | null   |
| 4  |  frank | null   |
| 5  |  sarah |  b     |
| 6  |  jamie |  b     |

再次考慮,您嘗試選擇前n個不同的過濾器。 只需為每個過濾器找到最小的ID並編號即可:

DECLARE @A TABLE(id INT, name VARCHAR(100), filter VARCHAR(100));
INSERT INTO @A VALUES
(1, 'joe',     'y' ), -- 1st
(2, 'anna',    'x' ), -- 2nd
(3, 'mike',    NULL), -- 3rd
(4, 'frank',   NULL), -- 4th
(5, 'sarah',   'x' ),
(6, 'jamie',   'y' ),
(9, 'forrest', 'z' ); -- 5th

WITH filter_minid AS (
    SELECT filter, MIN(id) AS minid
    FROM @A
    GROUP BY filter, CASE WHEN filter IS NULL THEN id END
), filter_minid_number AS (
    SELECT filter, minid, ROW_NUMBER() OVER (ORDER BY minid) AS rn
    FROM filter_minid
)
SELECT *
FROM @A a 
INNER JOIN filter_minid_number ON a.filter = filter_minid_number.filter OR a.id = filter_minid_number.minid
WHERE rn <= 5 -- this is where you filter for n distinct ids

結果:

| id | name    | filter | filter | minid | rn |
|----|---------|--------|--------|-------|----|
| 1  | joe     | y      | y      | 1     | 1  |
| 2  | anna    | x      | x      | 2     | 2  |
| 3  | mike    | NULL   | NULL   | 3     | 3  |
| 4  | frank   | NULL   | NULL   | 4     | 4  |
| 5  | sarah   | x      | x      | 2     | 2  |
| 6  | jamie   | y      | y      | 1     | 1  |
| 9  | forrest | z      | z      | 9     | 5  |

您可以使用帶窗口的MIN()對具有相同過濾器的值進行分組(以及將NULL值分配給不同的組),然后使用DENSE_RANK()展平這些值,以便以后進行過濾。

IF OBJECT_ID('tempdb..#Values') IS NOT NULL
    DROP TABLE #Values

CREATE TABLE #Values (
    ID INT IDENTITY,
    Name VARCHAR(10),
    Filter VARCHAR(10))

INSERT INTO #Values (
    Name,
    Filter)
VALUES
    ('joe', 'a'),
    ('anna', 'a'),
    ('mike', NULL),
    ('frank', NULL),
    ('sarah', 'b'),
    ('jamie', 'b'),
    ('john', 'a')

DECLARE @v_TopFilter INT = 4 -- Your top filter here

;WITH MinimumByFilter AS
(
    SELECT
        V.*,
        MinimumIDByFilter = MIN(V.ID) OVER (
            PARTITION BY 
                V.Filter,
                CASE WHEN V.Filter IS NULL THEN V.ID END)
    FROM
        #Values AS V
),
DenseRank AS
(
    SELECT
        M.*,
        DenseRank = DENSE_RANK() OVER(ORDER BY M.MinimumIDByFilter ASC)
    FROM
        MinimumByFilter AS M
)
SELECT
    D.ID,
    D.Name,
    D.Filter
FROM
    DenseRank AS D
WHERE
    D.DenseRank <= @v_TopFilter
ORDER BY
    D.ID ASC

您可以在此處檢查函數返回的內容:

ID  Name    Filter  MinimumIDByFilter   DenseRank
1   joe     a       1                   1
2   anna    a       1                   1
7   john    a       1                   1
3   mike    NULL    3                   2
4   frank   NULL    4                   3
5   sarah   b       5                   4
6   jamie   b       5                   4

你可以試試看

DECLARE @Tbl TABLE ( id INT,  name  varchar(10), filter varchar(10))

INSERT INTO @Tbl VALUES
(1 ,'joe', 'a'),
(2 ,'anna', 'a'),
(3 ,'mike', null),
(4 ,'frank', null),
(5 ,'sarah', 'b'),
(6 ,'jamie', 'b')

DECLARE @TOP INT = 3

SELECT id, name, filter FROM 
    ( SELECT *, DENSE_RANK() OVER(ORDER BY SUB_RNK) RNK
        FROM ( SELECT *, 
            MIN(id) OVER(PARTITION BY ISNULL(filter,id) ) SUB_RNK
          FROM @Tbl ) T1
    ) T2
WHERE 
    T2.RNK <= @TOP

結果:(對於前3名)

id          name       filter
----------- ---------- ----------
1           joe        a
2           anna       a
3           mike       NULL
4           frank      NULL

使用子查詢

CREATE PROCEDURE `top` (IN x INT UNSIGNED)
BEGIN
select * from tableA where `filter` in (select distinct `filter` from tableA LIMIT x )
END

使用聯接

CREATE PROCEDURE `top` (IN x INT UNSIGNED)
    BEGIN
    select * from tableA A 
      join (select distinct `filter` from tableA LIMIT x ) AA
      on A.`filter` = AA.`filter`
END

您可以使用dense_rank()

select t.*
from (select t.*,
             dense_rank() over (order by filter,
                                         (case when filter is null then id end)
                                end) as seqnum
      from t
     ) t
where seqnum < ?  -- whatever your limit is;

如果您想為此使用top ,則可以將top with ties使用:

select top (?) with ties t.*
from (select t.*,
             dense_rank() over (order by filter,
                                         (case when filter is null then id end)
                                end) as seqnum
      from t
     ) t
order by seqnum;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM