MySQL отримує позицію рядка в ORDER BY


86

За допомогою наступної таблиці MySQL:

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

Як я можу вибрати один рядок І його позицію серед інших рядків у таблиці, якщо сортувати за name ASC. Отже, якщо дані таблиці виглядають так, при сортуванні за назвою:

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

Як я міг вибрати Betaрядок, отримуючи поточну позицію цього рядка? Результат, який я шукаю, буде приблизно таким:

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

Я можу зробити просте, SELECT * FROM tbl ORDER BY name ASCа потім перерахувати рядки в PHP, але здається марним завантажувати потенційно великий набір результатів лише для одного рядка.



Відповіді:


120

Використовуй це:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

... щоб отримати унікальне значення позиції. Це:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

... надасть краваткам однакову цінність. IE: Якщо на другому місці є два значення, вони обидва матимуть позицію 2, коли перший запит дасть позицію 2 одному з них, а 3 - іншому ...


@actual: Нічого сказати - немає альтернативи, окрім переходу до конкурента, який підтримує аналітичні функції (PostgreSQL, Oracle, SQL Server, DB2 ...)
OMG Ponies

2
@OMGPonies Просто забудь після коми position, але це ідеально.
pierallard

Я знаю, що це досить стара публікація, але мені потрібно подібне рішення. Однак при використанні внутрішніх об'єднань, групування та упорядкування, поле "position" їх ігнорує, і все значення змішується. Будь-які рішення?
Даніель

@Daniel, ти повинен задати нове запитання і, можливо, звернутися до цього.
PeerBr

@actual, оскільки це запит для одного рядка, тут не повинно бути значного занепокоєння щодо продуктивності. По-іншому, якщо ви намагаєтесь отримати ранги повного списку, але ви можете просто "обдурити" і використовувати неявний ранг, упорядковуючи за балами.
AgmLauncher

20

Це єдиний спосіб, про який я можу думати:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'

2
+1 Хороший трюк ... Але ви, ймовірно , хочете використовувати name <= 'Beta'замість
Daniel Vassallo

Цей підхід дасть однакові positionзначення для зв'язків.
OMG Ponies

(Видалено мій попередній коментар - я помилився) ... Що, якщо ви додасте LIMIT 1туди? У разі нічиєї ви отримаєте лише один ряд з останньою позицією краватки.
Даніель Вассалло

Якщо OP може гарантувати, що nameполе є унікальним - тоді немає причин робити запит більш складним. Якщо він не може - тоді почекаємо його очікувань на результат щодо пов’язаних імен.
zerkms

8

Якщо запит простий і розмір поверненого набору результатів потенційно великий, тоді ви можете спробувати розділити його на два запити.

Перший запит із вузьким критерієм фільтрації просто для отримання даних цього рядка, а другий запит використовує COUNTз WHEREпропозицією для обчислення позиції.

Наприклад у вашому випадку

Запит 1:

SELECT * FROM tbl WHERE name = 'Beta'

Запит 2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

Ми використовуємо цей підхід у таблиці із записом 2M, і це набагато масштабніше, ніж підхід OMG Ponies.


4

Інші відповіді здаються мені занадто складними.

Ось простий приклад , припустимо, у вас є таблиця зі стовпцями:

userid | points

і ви хочете відсортувати ідентифікатори користувачів за точками та отримати позицію рядка ("рейтинг" користувача), тоді ви використовуєте:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num дає вам позицію рядка (рейтинг).

Якщо у вас MySQL 8.0+, тоді ви можете використати ROW_NUMBER ()


2

Позиція рядка в таблиці відображає, скільки рядків є "кращими", ніж цільовий рядок.

Отже, ви повинні порахувати ці рядки.

ВИБЕРІТЬ КОЛИЧКУ (*) + 1 tableЗвідки name<'Бета'

У разі нічиєї повертається найвища позиція.

Якщо ви додасте ще один рядок з такою ж назвою "Beta" після існуючого рядка "Beta", тоді повернута позиція все одно буде 2, оскільки вони матимуть одне і те ж місце в класифікації.

Сподіваюся, це допоможе людям, які шукатимуть щось подібне в майбутньому, оскільки я вважаю, що власник питання вже вирішив свою проблему.


2

У мене дуже схоже питання, тому я не буду задавати того самого питання, але я поділюсь тут, що я зробив, мені також довелося використовувати групу за замовленням AVG. Є студенти з підписами та сокором, і мені довелося їх класифікувати (іншими словами, я спочатку розраховую AVG, потім замовляю їх у DESC, а потім нарешті мені потрібно було додати посаду (рейтинг для мене), отже, я зробив щось дуже схоже на найкращу відповідь тут, з невеликими змінами, які пристосовуються до моєї проблеми):

Нарешті я помістив positionстовпець (для мене) у зовнішній SELECT

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

Цю відповідь було легше зрозуміти, ніж прийняту. +1
Ерік Сістранд,

1

Я переглядав прийняту відповідь, і вона здавалася дещо складною, тому ось її спрощена версія.

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name

0

У мене є подібні типи задач, де мені потрібен ранг ( індекс ) таблиці order by votes desc. Наступне чудово працює для мене.

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)

-9

може бути те, що вам потрібно, це додати синтаксис

LIMIT

так використовуйте

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

якщо вам потрібен лише один рядок ..


ця відповідь не вирішить проблему тут. Ви можете видалити його
Даміан Рафаель Латтенеро,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.