SQL ORDER BY - 将具有相同值的列分组
SQL ORDER BY - Keep column with same value grouped
我很难用文字表达我想要的东西(这个标题是我能想到的最好的),使用模式更容易,但我会先尝试解释一下。
我想使用此规则通过查询进行排序:
- 按 "Group" 列对查询进行分组。
- 将每个组的 "Primary" 放在其组的顶部。
- 如果某行与主行具有相同的"Name",则将其放在主行之后。
- 按名称放置其余行
第 1、2 和 4 点是微不足道的。一个简单的 ORDER BY 技巧。但是我从来没有见过第3点的查询。
我已经阅读了一些关于 RANK() and ROW_NUMBER() 函数的内容,并尝试了它们,但我还没有设法创建我想要的输出。我开始怀疑是否有可能这样做。
无论如何,这里有一大块 SQL 来测试一下。任何帮助表示赞赏。如果您能找到更好的术语来描述这一点,请随时进行更正。
CREATE TABLE #TEMP
(
COL_GROUP INT,
COL_PRIMARY BIT,
COL_NAME VARCHAR(3)
)
INSERT INTO
#TEMP
VALUES
(1,1,'AAA'),
(2,0,'BBB'),
(2,1,'BBB'),
(1,0,'BBB'),
(1,0,'AAA'),
(2,0,'AAA')
SELECT
*
FROM
#TEMP
ORDER BY
COL_GROUP,
COL_PRIMARY DESC,
COL_NAME
DROP TABLE #TEMP
这给出了这个输出:
COL_GROUP COL_PRIMARY COL_NAME
========= =========== ========
1 1 AAA
1 0 AAA
1 0 BBB
2 1 BBB
2 0 AAA
2 0 BBB
我想要的是这个输出:
COL_GROUP COL_PRIMARY COL_NAME
========= =========== ========
1 1 AAA
1 0 AAA
1 0 BBB
2 1 BBB
2 0 BBB -- The ones with the same name as the primary first
2 0 AAA
使用max
window 函数为col_primary = 1 获取col_name 并在order by
中的case
表达式中使用它对于所需的顺序。
SELECT * FROM (
SELECT
t.*,max(case when col_primary=1 then col_name end) over(partition by col_group) as prim_col_name
FROM
#TEMP t
) t
ORDER BY
COL_GROUP,
COL_PRIMARY DESC,
case when COL_NAME = prim_col_name then 1 else 2 end,
COL_NAME
这假设每个 col_primary=1 每个 col_group.
只能有一行
我想你正在寻找这个:
SELECT
*
FROM #TEMP o
ORDER BY COL_GROUP, COL_PRIMARY DESC, CASE WHEN COL_NAME = (SELECT COL_Name FROM #Temp i WHERE i.COL_PRimary = 1 AND i.Col_Group = o.Col_Group) THEN 0 ELSE 1 END, COL_NAME;
基本上,我正在做一个嵌套的 select 内部引用,对与外部相同但限制范围的数据集说:"I want the name where it matches the leader of the group and then make it 0 else 1"。这适用于小型数据集,但正如另一位用户所展示的那样,如果它是大型数据集,您可能希望将其分解为 CTE 并稍后调用它。如果你只做几千行或更少,这应该没问题。
根据评论解决
You could do a self-join and test if the name is equal to the primary, and if so assign the value of e.g. 1 else 0, then use that column in your order by, before the name column.
SELECT
T1.*
FROM
#TEMP T1 JOIN #TEMP T2
ON T1.COL_GROUP=T2.COL_GROUP AND T2.COL_PRIMARY=1
ORDER BY
T1.COL_GROUP,
T1.COL_PRIMARY DESC,
CASE WHEN T1.COL_NAME=T2.COL_NAME THEN 1 ELSE 0 END DESC,
T1.COL_NAME
在 table 本身上使用 left join 将确保您不会丢失任何记录,以防组中没有主要项目(可能是不是问题)。
这也将简化“order by”,只需添加链接列 T2.COL_NAME(如果 COL_NAME 相同,则不会为空作为主要)。
SELECT
T1.*
FROM
#TEMP T1
left join #TEMP T2 on T2.COL_GROUP = T1.COL_GROUP and T2.COL_NAME = T1.COL_NAME and T2.COL_PRIMARY = 1
ORDER BY
T1.COL_GROUP,
T1.COL_PRIMARY DESC,
T2.COL_NAME DESC,
T1.COL_NAME
我很难用文字表达我想要的东西(这个标题是我能想到的最好的),使用模式更容易,但我会先尝试解释一下。
我想使用此规则通过查询进行排序:
- 按 "Group" 列对查询进行分组。
- 将每个组的 "Primary" 放在其组的顶部。
- 如果某行与主行具有相同的"Name",则将其放在主行之后。
- 按名称放置其余行
第 1、2 和 4 点是微不足道的。一个简单的 ORDER BY 技巧。但是我从来没有见过第3点的查询。
我已经阅读了一些关于 RANK() and ROW_NUMBER() 函数的内容,并尝试了它们,但我还没有设法创建我想要的输出。我开始怀疑是否有可能这样做。
无论如何,这里有一大块 SQL 来测试一下。任何帮助表示赞赏。如果您能找到更好的术语来描述这一点,请随时进行更正。
CREATE TABLE #TEMP
(
COL_GROUP INT,
COL_PRIMARY BIT,
COL_NAME VARCHAR(3)
)
INSERT INTO
#TEMP
VALUES
(1,1,'AAA'),
(2,0,'BBB'),
(2,1,'BBB'),
(1,0,'BBB'),
(1,0,'AAA'),
(2,0,'AAA')
SELECT
*
FROM
#TEMP
ORDER BY
COL_GROUP,
COL_PRIMARY DESC,
COL_NAME
DROP TABLE #TEMP
这给出了这个输出:
COL_GROUP COL_PRIMARY COL_NAME
========= =========== ========
1 1 AAA
1 0 AAA
1 0 BBB
2 1 BBB
2 0 AAA
2 0 BBB
我想要的是这个输出:
COL_GROUP COL_PRIMARY COL_NAME
========= =========== ========
1 1 AAA
1 0 AAA
1 0 BBB
2 1 BBB
2 0 BBB -- The ones with the same name as the primary first
2 0 AAA
使用max
window 函数为col_primary = 1 获取col_name 并在order by
中的case
表达式中使用它对于所需的顺序。
SELECT * FROM (
SELECT
t.*,max(case when col_primary=1 then col_name end) over(partition by col_group) as prim_col_name
FROM
#TEMP t
) t
ORDER BY
COL_GROUP,
COL_PRIMARY DESC,
case when COL_NAME = prim_col_name then 1 else 2 end,
COL_NAME
这假设每个 col_primary=1 每个 col_group.
只能有一行我想你正在寻找这个:
SELECT
*
FROM #TEMP o
ORDER BY COL_GROUP, COL_PRIMARY DESC, CASE WHEN COL_NAME = (SELECT COL_Name FROM #Temp i WHERE i.COL_PRimary = 1 AND i.Col_Group = o.Col_Group) THEN 0 ELSE 1 END, COL_NAME;
基本上,我正在做一个嵌套的 select 内部引用,对与外部相同但限制范围的数据集说:"I want the name where it matches the leader of the group and then make it 0 else 1"。这适用于小型数据集,但正如另一位用户所展示的那样,如果它是大型数据集,您可能希望将其分解为 CTE 并稍后调用它。如果你只做几千行或更少,这应该没问题。
根据评论解决
You could do a self-join and test if the name is equal to the primary, and if so assign the value of e.g. 1 else 0, then use that column in your order by, before the name column.
SELECT
T1.*
FROM
#TEMP T1 JOIN #TEMP T2
ON T1.COL_GROUP=T2.COL_GROUP AND T2.COL_PRIMARY=1
ORDER BY
T1.COL_GROUP,
T1.COL_PRIMARY DESC,
CASE WHEN T1.COL_NAME=T2.COL_NAME THEN 1 ELSE 0 END DESC,
T1.COL_NAME
在 table 本身上使用 left join 将确保您不会丢失任何记录,以防组中没有主要项目(可能是不是问题)。
这也将简化“order by”,只需添加链接列 T2.COL_NAME(如果 COL_NAME 相同,则不会为空作为主要)。
SELECT
T1.*
FROM
#TEMP T1
left join #TEMP T2 on T2.COL_GROUP = T1.COL_GROUP and T2.COL_NAME = T1.COL_NAME and T2.COL_PRIMARY = 1
ORDER BY
T1.COL_GROUP,
T1.COL_PRIMARY DESC,
T2.COL_NAME DESC,
T1.COL_NAME