MySQL没有提供排名函数,但是我们可以通过一些技巧来实现开窗函数的效果。
-
CREATE
TABLE
`tem` (
-
`id`
int(
11)
NOT
NULL AUTO_INCREMENT,
-
`str`
char(
1)
DEFAULT
NULL,
-
PRIMARY
KEY (
`id`)
-
)
ENGINE=
InnoDB AUTO_INCREMENT=
9
DEFAULT
CHARSET=utf8;
-
-
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
1,
'A');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
2,
'B');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
3,
'A');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
4,
'C');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
5,
'A');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
6,
'C');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
7,
'B');
-
INSERT
INTO
`test`.
`tem`(
`id`,
`str`)
VALUES (
8,
'A');
num |
id |
str |
---|---|---|
1 | 1 | A |
2 | 3 | A |
3 | 5 | A |
4 | 8 | A |
1 | 2 | B |
2 | 7 | B |
1 | 4 | C |
2 | 6 | C |
-
SELECT
-
count(*)
num,
-
t1.*
-
FROM
-
tem t1
-
INNER
JOIN tem t2
ON t1.str = t2.str
AND t1.id >= t2.id
-
GROUP
BY
-
t1.id
-
ORDER
BY
-
t1.str, t1.id;
让我们看看这个查询的执行计划
-
+----+-------------+-------+------+---------------+------+---------+------+------+-------------------------------------------------+
-
| id | select_type
| table | type
| possible_keys | key
| key_len | ref
| rows | Extra
|
-
+----+-------------+-------+------+---------------+------+---------+------+------+-------------------------------------------------+
-
|
1
| SIMPLE | t1
| ALL | PRIMARY
| NULL | NULL
| NULL |
8
| Using temporary; Using filesort |
-
| 1 | SIMPLE
| t2 | ALL
| PRIMARY | NULL
| NULL | NULL
| 8 | Using where; Using join buffer (flat, BNL join)
|
-
+----+-------------+-------+------+---------------+------+---------+------+------+-------------------------------------------------+
-
2 rows in set (0.06 sec)
非常低效:临时表、文件排序、循环嵌套都用上了
-
SELECT
-
(
SELECT
COUNT(*)
FROM tem t2
WHERE t1.str = t2.str
AND t1.id >= t2.id)
num,
-
t1.*
-
FROM
-
tem t1
-
ORDER
BY
-
t1.str, t1.id;
再看看执行计划
-
+----+--------------------+-------+------+---------------+------+---------+------+------+----------------+
-
| id | select_type
| table | type
| possible_keys | key
| key_len | ref
| rows | Extra
|
-
+----+--------------------+-------+------+---------------+------+---------+------+------+----------------+
-
|
1
| PRIMARY | t1
| ALL | NULL
| NULL | NULL
| NULL |
8
| Using filesort |
-
| 2 | DEPENDENT SUBQUERY
| t2 | ALL
| PRIMARY | NULL
| NULL | NULL
| 8 | Using where
|
-
+----+--------------------+-------+------+---------------+------+---------+------+------+----------------+
-
2 rows in set (0.06 sec)
很好,两次全扫描,而且只用到了文件排序,如果加上索引,文件排序也可以避免。
-
SELECT
-
@
num :=
IF(@
str =
str, @
num +
1,
1)
num,
-
id,
-
@
str :=
str
str
-
FROM
-
tem, (
SELECT @
str :=
'', @
num :=
0) t1
-
ORDER
BY
-
str,
id;
执行计划
-
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
-
| id | select_type
| table | type
| possible_keys | key
| key_len | ref
| rows | Extra
|
-
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
-
|
1
| PRIMARY |
| system | NULL
| NULL | NULL
| NULL |
1
| |
-
| 1 | PRIMARY
| tem | ALL
| NULL | NULL
| NULL | NULL
| 8 | Using filesort
|
-
|
2
| DERIVED | NULL
| NULL | NULL
| NULL | NULL
| NULL | NULL
| No tables used |
-
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
-
3 rows
in set (
0.
06 sec)
效率最高,一次全扫描搞定。文件排序是因为没有索引。