Как с помощью 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.