Как с помощью MySQL создать столбец, содержащий индекс записи в таблице?


100

Есть ли способ получить фактический номер строки из запроса?

Я хочу иметь возможность упорядочить таблицу с именем league_girl по полю с именем score; и вернуть имя пользователя и фактическую позицию строки этого имени пользователя.

Я хочу ранжировать пользователей, чтобы я мог сказать, где находится конкретный пользователь, т.е. Джо занимает 100 позицию из 200, т.е.

User Score Row
Joe  100    1
Bob  50     2
Bill 10     3

Я видел здесь несколько решений, но я пробовал большинство из них, и ни одно из них на самом деле не возвращает номер строки.

Я пробовал это:

SELECT position, username, score
FROM (SELECT @row := @row + 1 AS position, username, score 
       FROM league_girl GROUP BY username ORDER BY score DESC) 

Как получено

... но, похоже, он не возвращает позицию строки.

Любые идеи?


строка - это имя поля или вы хотите упорядочить по первичному ключу?
— Sarfraz

В SQL номера строк на самом деле не важны. Что вам нужно сделать, так это добавить в таблицу первичный ключ с автоматическим увеличением.
— simendsjo

9
Первичный ключ НИКОГДА не должен быть идентификатором строки, поскольку они не являются надежными для фактического положения строки.
— TheBounder

3
Кроме того, поскольку номер строки будет функцией оценки, которая, как я полагаю, не является статическим значением, создание автоматически увеличиваемого значения (первичный ключ или нет) не даст желаемого результата.
— kasperjj

вы можете сохранить уродливое для пользовательской функции, см. datamakessense.com/mysql-rownum-row-number-function
— AdrianBR

Ответы:


174

Вы можете попробовать следующее:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r;

Эта JOIN (SELECT @curRow := 0)часть позволяет инициализировать переменную, не требуя отдельной SETкоманды.

Прецедент:

CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);

Тестовый запрос:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r
WHERE   l.score > 50;

Результат:

+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
|        3 | c        |    75 |          1 |
|        5 | e        |    55 |          2 |
|        6 | f        |    80 |          3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)

19
Вы также можете инициализировать @curRow, заменив JOINоператор запятой, например:FROM league_girl l, (SELECT @curRow := 0) r
— Майк,

2
@ Майк: Это правда. И это, наверное, тоже аккуратнее. Спасибо, что поделились этим советом.
— Daniel Vassallo

2
@smhnaji MySQL требует, чтобы каждой «производной таблице» было присвоено имя. Я решил назвать его "r" :) ... В данном случае он не имеет особого смысла, но вы обычно используете его для ссылки на атрибуты производной таблицы, как если бы это была настоящая таблица.
— Даниэль Вассалло

20
Люди должны знать, что этот номер строки вычисляется до того, как будет выполнено какое-либо упорядочение, поэтому числа могут смешаться, если порядок изменяет порядок строк.
— Мрачный ...

7
Есть ли способ рассчитать этот номер строки после ORDER BY?
— Pierre de LESPINAY

38
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo

есть ли способ сделать это, чтобы столбец итератора был целым, а не десятичным?
— kraftydevil

7

Вот структура шаблона, который я использовал:

  select
          /*this is a row number counter*/
          ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
          as rownumber,
          d3.*
  from 
  ( select d1.* from table_name d1 ) d3

А вот мой рабочий код:

select     
           ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
           as rownumber,
           d3.*
from
(   select     year( d1.date ), month( d1.date ), count( d1.id )
    from       maindatabase d1
    where      ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
    group by   YEAR( d1.date ), MONTH( d1.date ) ) d3

идеально, работает как шарм, а шаблон можно повторно использовать с подзапросом в качестве параметра ..
— Заваэль

Это решение также работает, если в вашем базовом запросе используется GROUP BY
— Дэйв Р.

4

Вы также можете использовать

SELECT @curRow := ifnull(@curRow,0) + 1 Row, ...

для инициализации переменной счетчика.


3
@curRowможет все еще иметь значение с момента последнего выполнения этого запроса в текущем сеансе.
— Билл Карвин

Верно, но только если вы повторно запрашиваете тот же экземпляр подключения. Локальные переменные автоматически удаляются после закрытия соединения.
— Hearth

Да, именно это я имел в виду, когда сказал «в текущем сеансе».
— Билл Карвин

3

Предполагая, что MySQL поддерживает это, вы можете легко сделать это с помощью стандартного подзапроса SQL:

select 
    (count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
    username,
    score
from league_girl l2
order by score;

Для большого количества отображаемых результатов это будет немного медленным, и вы захотите вместо этого переключиться на самостоятельное соединение.


3

Если вы просто хотите узнать позицию одного конкретного пользователя после упорядочения по количеству полей, вы можете просто выбрать всю строку в своей таблице, где оценка поля выше, чем текущая оценка пользователя. И используйте номер строки, возвращенный + 1, чтобы узнать, какая позиция этого текущего пользователя.

Предполагая, что ваша таблица league_girlи ваше основное поле есть id, вы можете использовать это:

SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>

0

Я нашел исходный ответ невероятно полезным, но я также хотел получить определенный набор строк на основе номеров строк, которые я вставлял. Таким образом, я заключил весь исходный ответ в подзапрос, чтобы я мог ссылаться на номер строки, которую я вставлял.

SELECT * FROM 
( 
    SELECT *, @curRow := @curRow + 1 AS "row_number"
    FROM db.tableName, (SELECT @curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;

Наличие подзапроса в подзапросе не очень эффективно, поэтому стоит проверить, получите ли вы лучший результат, если ваш SQL-сервер обработает этот запрос, или извлечет всю таблицу и заставит приложение / веб-сервер манипулировать строками после факта .

Лично мой SQL-сервер не слишком загружен, поэтому было предпочтительнее обрабатывать вложенные подзапросы.


0

Я знаю, что OP просит mysqlответа, но поскольку я обнаружил, что другие ответы не работают для меня,

  • Большинство из них терпят неудачу с order by
  • Или они просто очень неэффективны и делают ваш запрос очень медленным для толстой таблицы

Поэтому, чтобы сэкономить время для таких, как я, просто проиндексируйте строку после получения их из базы данных.

пример в PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1;
}

пример в PHP с использованием смещения и ограничения для разбиения по страницам:

$limit = 20; //page size
$offset = 3; //page number

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1+($limit*($offset-1));
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.