在MySQL中有一种很好的方法来复制SQL Server函数ROW_NUMBER()
吗?
例如:
SELECT
col1, col2,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1
然后,我可以添加一个条件,将intRow
限制为1,以便为每个col3
对获得最高(col1, col2)
的单行。
答案 0 :(得分:193)
MySQL中没有排名功能。最接近的是使用变量:
SELECT t.*,
@rownum := @rownum + 1 AS rank
FROM YOUR_TABLE t,
(SELECT @rownum := 0) r
那么在我的案例中如何运作呢?我需要两个变量,col1和col2各有一个?当col1改变时,Col2需要以某种方式重置..?
是。如果是Oracle,则可以使用LEAD函数在下一个值处达到峰值。值得庆幸的是,Quassnoi涵盖了the logic for what you need to implement in MySQL。
答案 1 :(得分:94)
对于每个(col1,col2)对,我想要具有单个最高col3的行。
这是一个groupwise maximum,这是最常见的SQL问题之一(因为它看起来应该很简单,但实际上并非如此)。
我常常喜欢null-self-join:
SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;
“获取表格中没有与col1匹配的其他行,col2具有更高col3的行。”(您将注意到这一点,并且如果多行具有相同的行,则大多数其他groupwise-maximum解决方案将返回多行col1,col2,col3。如果这是一个问题,您可能需要进行一些后期处理。)
答案 2 :(得分:78)
我总是最终遵循这种模式。鉴于此表:
+------+------+
| i | j |
+------+------+
| 1 | 11 |
| 1 | 12 |
| 1 | 13 |
| 2 | 21 |
| 2 | 22 |
| 2 | 23 |
| 3 | 31 |
| 3 | 32 |
| 3 | 33 |
| 4 | 14 |
+------+------+
你可以得到这个结果:
+------+------+------------+
| i | j | row_number |
+------+------+------------+
| 1 | 11 | 1 |
| 1 | 12 | 2 |
| 1 | 13 | 3 |
| 2 | 21 | 1 |
| 2 | 22 | 2 |
| 2 | 23 | 3 |
| 3 | 31 | 1 |
| 3 | 32 | 2 |
| 3 | 33 | 3 |
| 4 | 14 | 1 |
+------+------+------------+
通过运行此查询,该查询不需要定义任何变量:
SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j
希望有所帮助!
答案 3 :(得分:58)
SELECT
@i:=@i+1 AS iterator,
t.*
FROM
tablename AS t,
(SELECT @i:=0) AS foo
答案 4 :(得分:26)
查看这篇文章,它展示了如何在MySQL中使用分区模仿SQL ROW_NUMBER()。我在WordPress实现中遇到了同样的情况。我需要ROW_NUMBER()并且它不存在。
http://www.explodybits.com/2011/11/mysql-row-number/
本文中的示例是按字段使用单个分区。要通过其他字段进行分区,您可以执行以下操作:
SELECT @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
,t.col1
,t.col2
,t.Col3
,t.col4
,@prev_value := concat_ws('',t.col1,t.col2)
FROM table1 t,
(SELECT @row_num := 1) x,
(SELECT @prev_value := '') y
ORDER BY t.col1,t.col2,t.col3,t.col4
使用concat_ws处理null。我使用int,date和varchar对3个字段进行了测试。希望这可以帮助。查看文章,因为它打破了这个查询并解释了它。
答案 5 :(得分:18)
从MySQL 8.0.0
及以上,您可以原生使用窗口函数。
窗口功能。
MySQL现在支持窗口函数,对于查询中的每一行,它使用与该行相关的行执行计算。这些包括RANK(),LAG()和NTILE()等函数。此外,现在可以将几个现有的聚合函数用作窗函数;例如,SUM()和AVG()。
返回其分区中当前行的编号。行数从1到分区行数。
ORDER BY会影响行的编号顺序。没有ORDER BY,行编号是不确定的。
演示:
CREATE TABLE Table1(
id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);
INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
(2,1,'x'),(2,1,'y'),(2,2,'z');
SELECT
col1, col2,col3,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;
<强> DBFiddle Demo 强>
答案 6 :(得分:15)
我还会投票支持Mosty Mostacho的解决方案,对他的查询代码进行微小的修改:
SELECT a.i, a.j, (
SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a
哪个会得到相同的结果:
+------+------+------------+
| i | j | row_number |
+------+------+------------+
| 1 | 11 | 1 |
| 1 | 12 | 2 |
| 1 | 13 | 3 |
| 2 | 21 | 1 |
| 2 | 22 | 2 |
| 2 | 23 | 3 |
| 3 | 31 | 1 |
| 3 | 32 | 2 |
| 3 | 33 | 3 |
| 4 | 14 | 1 |
+------+------+------------+
表格:
+------+------+
| i | j |
+------+------+
| 1 | 11 |
| 1 | 12 |
| 1 | 13 |
| 2 | 21 |
| 2 | 22 |
| 2 | 23 |
| 3 | 31 |
| 3 | 32 |
| 3 | 33 |
| 4 | 14 |
+------+------+
唯一的区别是查询不使用JOIN和GROUP BY,而是依赖于嵌套选择。
答案 7 :(得分:11)
我会定义一个函数:
delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$
然后我可以做:
select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;
现在你没有子视图,你在视图中没有它。
答案 8 :(得分:8)
在MySQL中没有像rownum
,row_num()
这样的功能,但方法如下:
select
@s:=@s+1 serial_no,
tbl.*
from my_table tbl, (select @s:=0) as s;
答案 9 :(得分:6)
查询mysql中的row_number
viewDidLoad()
答案 10 :(得分:4)
我发现最好的解决方案是使用这样的子查询:
SELECT
col1, col2,
(
SELECT COUNT(*)
FROM Table1
WHERE col1 = t1.col1
AND col2 = t1.col2
AND col3 > t1.col3
) AS intRow
FROM Table1 t1
PARTITION BY列只是与'='进行比较并用AND分隔。 ORDER BY列将与'&lt;'进行比较或'&gt;',并以OR分隔。
我发现这很灵活,即使它有点贵。
答案 11 :(得分:4)
无法模仿rownumber功能。您可能会得到您期望的结果,但您很可能会在某个阶段感到失望。 以下是mysql文档所说的内容:
对于其他语句,例如SELECT,您可能会得到您期望的结果,但这不能保证。在下面的语句中,您可能会认为MySQL将首先评估@a,然后再进行一次分配: SELECT @ a,@ a:= @ a + 1,...; 但是,涉及用户变量的表达式的评估顺序是未定义的。
此致 的Georgi。
答案 12 :(得分:3)
MariaDB 10.2正在实现&#34; Window Functions&#34;,包括RANK(),ROW_NUMBER()和其他一些东西:
https://mariadb.com/kb/en/mariadb/window-functions/
根据Percona Live本月的一次演讲,他们已经得到了相当好的优化。
语法与问题中的代码相同。
答案 13 :(得分:2)
我没有看到任何关于“PARTITION BY”部分的简单答案,所以这是我的:
Int8 *data[4]; //array can hold maximum of 4 elements
在这个简单的例子中我只放了一个,但你可以有几个“PARTITION BY”部分
SELECT
*
FROM (
select
CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
, @partitionBy_1:=l AS p
, t.*
from (
select @row_number:=0,@partitionBy_1:=null
) as x
cross join (
select 1 as n, 'a' as l
union all
select 1 as n, 'b' as l
union all
select 2 as n, 'b' as l
union all
select 2 as n, 'a' as l
union all
select 3 as n, 'a' as l
union all
select 3 as n, 'b' as l
) as t
ORDER BY l, n
) AS X
where i > 1
答案 14 :(得分:1)
也有点晚了,但今天我有同样的需求,所以我在谷歌搜索,最后在Pinal Dave的文章http://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/中找到了一个简单的一般方法
我想专注于Paul的原始问题(这也是我的问题)所以我总结了我的解决方案作为一个工作示例。
因为我们想要对两列进行分区,所以我会在迭代过程中创建一个SET变量,以确定是否已启动新组。
SELECT col1, col2, col3 FROM (
SELECT col1, col2, col3,
@n := CASE WHEN @v = MAKE_SET(3, col1, col2)
THEN @n + 1 -- if we are in the same group
ELSE 1 -- next group starts so we reset the counter
END AS row_number,
@v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group
3表示MAKE_SET的第一个参数,我想要SET中的两个值(3 = 1 | 2)。 当然,如果我们没有两列或更多列构造组,我们可以消除MAKE_SET操作。结构完全一样。这对我来说很有用。非常感谢Pinal Dave的明确演示。
答案 15 :(得分:1)
这允许在MySQL中实现与ROW_NUMBER()和PARTITION BY相同的功能
SELECT @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
FirstName,
Age,
Gender,
@prev_value := GENDER
FROM Person,
(SELECT @row_num := 1) x,
(SELECT @prev_value := '') y
ORDER BY Gender, Age DESC
答案 16 :(得分:1)
有点晚,但也可能对寻找答案的人有帮助......
rows / row_number示例 - 可以在任何SQL中使用的递归查询:
WITH data(row_num, some_val) AS
(
SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
UNION ALL
SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
WHERE row_num BETWEEN 5 AND 10
/
ROW_NUM SOME_VAL
-------------------
5 11
6 16
7 22
8 29
9 37
10 46
答案 17 :(得分:1)
这也可以是一个解决方案:
SET @row_number = 0;
SELECT
(@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
employees
答案 18 :(得分:1)
如果您的查询包含 GROUP BY
语句,则使用交叉连接和逗号的解决方案将不起作用。对于这种情况,您可以使用子选择:
SELECT (@row_number := @row_number + 1) AS rowNumber, res.*
FROM
(
SELECT SUM(r.amount)
FROM Results r
WHERE username = 1
GROUP BY r.amount
) res
CROSS JOIN (SELECT @row_number := 0) AS dummy
答案 19 :(得分:0)
这里大部分/全部使用查询中变量的答案似乎忽略了文档所说的事实(解释):
不要依赖SELECT列表中从上到下顺序评估的项目。不要在一个SELECT项中分配变量,而在另一个SELECT项中使用它们
因此,有可能冒出错误答案的风险,因为他们通常会做
select
(row number variable that uses partition variable),
(assign partition variable)
如果对这些内容进行了自下而上的评估,则行号将停止工作(无分区)
所以我们需要使用可以保证执行顺序的东西。输入案例的时间:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
如大纲ld所示,prevcol的分配顺序很重要-必须先将prevcol与当前行的值进行比较,然后才能从当前行为其分配值(否则它将是当前行的col值,而不是前一行的列值)。
这是如何组合在一起的:
第一个WHEN被评估。如果此行的col与上一行的col相同,则@r递增并从CASE返回。此返回的led值存储在@r中。 MySQL的一个功能是赋值将分配给@r的新值返回到结果行中。
对于结果集的第一行,@prevcol为null(在子查询中被初始化为null),因此该谓词为false。每次col更改时,该第一个谓词也返回false(当前行与上一行不同)。这将导致对第二个WHEN进行评估。
第二个WHEN谓词始终为false,它的存在纯粹是为@prevcol分配一个新值。由于此行的col与上一行的col不同(我们知道这是因为如果相同,则将使用第一个WHEN),因此我们必须分配新值以使其下次进行测试。因为进行了赋值,然后将赋值的结果与null进行比较,并且等于null的任何内容均为false,所以该谓词始终为false。但是至少要评估一下,它的工作是保留此行中col的值,因此可以针对下一行的col值进行评估
因为第二个WHEN为假,这意味着在我们按(col)进行分区的列已更改的情况下,正是ELSE为@r提供了新值,从而从1重新开始编号
我们遇到了以下情况:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
具有一般形式:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
脚注:
pcol中的p表示“分区”,ocol中的o表示“顺序”-通常,我从变量名中删除了“ prev”以减少视觉混乱
(@pcolX := colX) = null
周围的括号很重要。没有它们,您将为@pcolX分配null,事情将停止工作
妥协的是,结果集也必须按分区列排序,以便比较前一列。因此,您无法将行号按一列排序,而将结果集按另一列排序。您也许可以使用子查询来解决此问题,但我相信文档也指出,除非使用LIMIT,否则子查询的排序可能会被忽略,这可能会影响性能
除了测试该方法是否有效之外,我还没有深入研究它,但是如果存在第二个WHEN中的谓词将被优化的风险(与null相比的任何结果都是null / false,那么为什么还要花时间运行)分配)并且未执行,它也会停止。根据我的经验,这似乎没有发生,但我很乐意接受评论并提出解决方案,如果可能的话
在创建@pcolX变量的子查询中,将创建@pcolX的空值强制转换为列的实际类型可能是明智的,即:select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
答案 20 :(得分:0)
自版本 8.0 + 起,MySQL已支持ROW_NUMBER()。
如果使用MySQL 8.0或更高版本,请检查ROW_NUMBER()函数。 否则,您将模拟ROW_NUMBER()函数。
row_number()是一个排序函数,它返回一行的序号,从第一行的1开始。
对于旧版本,
SELECT t.*,
@rowid := @rowid + 1 AS ROWID
FROM TABLE t,
(SELECT @rowid := 0) dummy;
答案 21 :(得分:0)
这不是最可靠的解决方案-但是,如果您只是想在仅包含几个不同值的字段上创建分区等级,那么在逻辑中使用与您一样多的变量时,可能会不明智地使用这种情况要求。
过去类似的事情对我有用:
SELECT t.*,
CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1
WHEN <partition_field> = @rownum2 := @rownum2 + 1
...
END AS rank
FROM YOUR_TABLE t,
(SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;
有希望/有帮助的希望!
答案 22 :(得分:-1)
set @i = 1;
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID)
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID()
FROM TEMP_ARG_VALUE_LOOKUP
order by ARGUMENT_NAME;
答案 23 :(得分:-1)
当我们有多个列时,这个工作非常适合我创建RowNumber。在这种情况下有两列。
SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber,
`Fk_Business_Unit_Code`,
`NetIQ_Job_Code`,
`Supervisor_Name`,
@prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`
FROM Employee
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,
(SELECT @row_num := 1) x,
(SELECT @prev_value := '') y
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC
答案 24 :(得分:-5)
SELECT
col1, col2,
count(*) as intRow
FROM Table1
GROUP BY col1,col2
ORDER BY col3 desc