MySQL은 ORDER BY에서 행 위치를 얻습니다.


88

다음 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에서 행을 열거 할 수 있지만 단일 행에 대해 잠재적으로 큰 결과 집합을로드하는 것은 낭비 인 것 같습니다.



답변:


122

이것을 사용하십시오 :

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의 위치를, 다른 하나에 3의 위치를 ​​제공 할 때 둘 다 위치가 2가됩니다.


@actual : 대답 거기에 아무것도 - 대안이 경쟁사로 이동 이외, 없다가 지원하는 분석 기능 (PostgreSQL을, 오라클, SQL 서버, DB2 ...)
OMG 조랑말

2
@OMGPonies 뒤에 쉼표를 잊어 버려도 position완벽합니다.
pierallard 2014 년

꽤 오래된 게시물이라는 것을 알고 있지만 비슷한 솔루션이 필요합니다. 그러나 내부 조인, 그룹화 기준 및 정렬 기준을 사용하는 경우 "위치"필드가이를 무시하고 값이 모두 섞여 있습니다. 해결책이 있습니까?
Daniel

@Daniel 당신은 새로운 질문을하고 아마도 이것을 참조해야합니다.
PeerBr 2015

@actual, 이것은 단일 행에 대한 쿼리이므로 여기서 중요한 성능 문제가 없어야합니다. 전체 목록의 순위를 얻으려는 경우에는 다르지만 "속임수"를 사용하고 점수를 기준으로 정렬하여 암시 적 순위를 사용할 수 있습니다.
AgmLauncher 2015 년

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거기에 추가하면 어떻게 되나요? 동점의 경우, 동점의 마지막 위치에 한 행만 표시됩니다.
Daniel Vassallo

OP가 name필드가 고유 하다는 것을 보장 할 수 있다면 쿼리를 더 복잡하게 만들 이유가 없습니다. 그가 할 수 없다면-동점 이름에 대한 그의 결과 기대를 기다리 자.
zerkms

8

쿼리가 단순하고 반환 된 결과 집합의 크기가 잠재적으로 큰 경우이를 두 개의 쿼리로 분할 할 수 있습니다.

축소 필터링 기준이있는 첫 번째 쿼리는 해당 행의 데이터 만 검색하고 두 번째 쿼리는 COUNTwith WHERE절을 사용하여 위치를 계산합니다.

예를 들어 귀하의 경우

쿼리 1 :

SELECT * FROM tbl WHERE name = 'Beta'

쿼리 2 :

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

2M 레코드가있는 테이블에서이 접근 방식을 사용하며 이는 OMG Ponies의 접근 방식보다 확장 성이 훨씬 뛰어납니다.


5

다른 답변은 너무 복잡해 보입니다.

다음은 간단한 예 입니다. 열이있는 테이블이 있다고 가정 해 보겠습니다.

userid | points

사용자 ID를 포인트별로 정렬하고 행 위치 (사용자의 "순위")를 얻으려면 다음을 사용합니다.

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

테이블에서 행의 위치는 대상 행보다 "더 나은"행 수를 나타냅니다.

따라서 해당 행을 계산해야합니다.

SELECT COUNT (*) + 1 from tableWHERE name< '베타'

동점 일 경우 가장 높은 순위가 반환됩니다.

기존 "Beta"행 뒤에 "Beta"라는 이름이 같은 다른 행을 추가하면 분류에서 동일한 위치를 공유하므로 반환 된 위치는 여전히 2입니다.

질문 소유자가 이미 문제를 해결했다고 생각하므로 향후 유사한 것을 검색하는 사람들에게 도움이되기를 바랍니다.


2

저에게는 매우 유사한 문제가 있으므로 동일한 질문을하지 않겠습니다.하지만 여기서 제가 한 일을 공유하고 AVG에서 그룹 별 및 주문을 사용해야했습니다. 서명과 소 코어가있는 학생이 있는데 순위를 매겨 야했습니다 (즉, 먼저 AVG를 계산 한 다음 DESC에서 주문한 다음 마지막으로 순위를 추가해야하므로 뭔가 매우 유사 은 AS 가장 좋은 대답 ) 내 문제에 적응 약간의 변화가 여기 :

마지막으로 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

나는 받아 들여진 대답을 살펴 보았고 약간 복잡해 보였으므로 여기에 단순화 된 버전이 있습니다.

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

0

나는 table의 rank ( Index )가 필요한 비슷한 유형의 문제가 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

한 줄만 필요한 경우 ..


이 대답은 여기서 문제를 해결하지 못합니다. 당신은 그것을 삭제 고려할 수 있습니다
의 Damián 라파엘 Lattenero에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.