2008-10-19 3 views
37

Zend Framework's database component에서 작업했을 때 MySQL, PostgreSQL 및 SQLite에서 지원하는 LIMIT 절의 기능을 추상화하려고 시도했습니다.Microsoft SQL Server 2000의 MySQL LIMIT 절을 에뮬레이트

$select = $db->select(); 
$select->from('mytable'); 
$select->order('somecolumn'); 
$select->limit(10, 20); 

데이터베이스가 LIMIT을 지원,이 다음과 같은 SQL 쿼리를 생성합니다 : 즉, 쿼리를 작성하는 것은이 방법으로 수행 될 수있다 데이터베이스의 브랜드이

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

된 더 복잡한을 그 LIMIT을 지원하지 않습니다 (그 절은 표준 SQL 언어의 일부가 아닙니다). 행 번호를 생성 할 수있는 경우 전체 쿼리를 파생 테이블로 만들고 외부 쿼리에서 BETWEEN을 사용하십시오. 이것이 Oracle과 IBM DB2의 솔루션이었습니다.

SELECT z2.* 
FROM (
    SELECT ROW_NUMBER OVER(ORDER BY id) AS zend_db_rownum, z1.* 
    FROM (...original SQL query...) z1 
) z2 
WHERE z2.zend_db_rownum BETWEEN @offset+1 AND @[email protected]; 

그러나, 마이크로 소프트 SQL 서버 2000 ROW_NUMBER() 기능이없는 하나는 쿼리이 방법을 쓸 수 있도록 마이크로 소프트 SQL 서버 2005, 유사한 행 번호 기능을 가지고 있습니다.

제 질문은 SQL을 사용하여 Microsoft SQL Server 2000의 LIMIT 기능을 에뮬레이트하는 방법을 생각해 낼 수 있습니까? 커서 또는 T-SQL 또는 저장 프로 시저를 사용하지 않고. 카운트와 오프셋 모두 LIMIT에 대한 두 인수를 모두 지원해야합니다. 임시 테이블을 사용하는 솔루션도 허용되지 않습니다.

편집 : MS SQL Server 2000의 가장 일반적인 솔루션은 예 (75)를 통해 행 (50)를 얻을 수 있도록, 아래와 같은 것 같다

:

SELECT TOP 25 * 
FROM ( 
    SELECT TOP 75 * 
    FROM table 
    ORDER BY BY field ASC 
) a 
ORDER BY field DESC; 

을하지만,이 '아무튼 총 결과 세트가 예를 들어 60 행이면 t 작업. 내부 질의는 60 행을 반환합니다. 그 이유는 이것이 외부 75 행에 있기 때문입니다. 그런 다음 외부 질의는 35-60 행을 반환합니다. 이는 원하는 "페이지"50-75에 맞지 않습니다. 기본적으로이 솔루션은 페이지 크기의 배수가 아닌 결과 집합의 마지막 "페이지"가 ​​필요하지 않는 한 작동합니다.

편집 :

또 다른 해결책은 잘 작동하지만 결과 집합을 가정 할 수있는 경우에만 고유 한 열을 포함

SELECT TOP n * 
FROM tablename 
WHERE key NOT IN (
    SELECT TOP x key 
    FROM tablename 
    ORDER BY key 
); 

결론 :

없음 일반 -purpose 솔루션은 MS SQL Server 2000에서 LIMIT을 에뮬레이트하는 것으로 나타납니다. ROW_NUMBER() 펑션을 사용할 수 있으면 좋은 해결책이 있습니다 MS SQL Server 2005의 경우.

답변

4
SELECT TOP n * 
FROM tablename 
WHERE key NOT IN (
    SELECT TOP x key 
    FROM tablename 
    ORDER BY key 
    DESC 
); 
+0

예,이 가까이이지만, 중간 쿼리 결과에 고유 키가있을 때만 작동합니다. GROUP BY 쿼리에서 또는 여러 테이블을 조인하는 쿼리를 어떻게 수행합니까? –

4

LIMIT 만 필요한 경우 ms sql에 해당하는 TOP 키워드가 있으므로 분명합니다. OFFSET을 사용하여 LIMIT이 필요한 경우 앞에서 설명한 것처럼 해킹을 시도 할 수 있지만 모든 오버 헤드가 추가됩니다. 즉, 한 가지 방법으로 주문한 다음 다른 주문 방식으로 주문할 수 있습니다. 모든 계단식이 필요하지 않다고 생각합니다. 내 의견에서 가장 깨끗한 솔루션은 SQL 측에서 오프셋없이 TOP을 사용하고 php의 mssql_data_seek와 같은 적절한 클라이언트 메소드를 사용하여 필요한 시작 레코드를 찾는 것입니다.이것은 순수한 SQL 솔루션은 아니지만 오버 헤드를 추가하지 않기 때문에 가장 좋은 솔루션이라고 생각합니다. 건너 뛴 레코드는 네트워크를 통해 전달 될 수 없습니다.).

+0

신청서를 디자인 할 때 귀하의 제안에 동의합니다. 필자는 일관된 API를 가지고 있지만 다른 데이터베이스 브랜드에 필요한 다른 SQL을 생성해야하는 데이터베이스 액세스 프레임 워크를 설계했습니다. 솔루션은 가져 오기가 아닌 SQL 준비 단계에 있어야했습니다. –

+1

좋은 생각, 고마워. 전반적으로 나는 당신의 솔루션이 더 실용적이라고 생각합니다. – 0plus1

+0

그래서 나는 MSSQL을 싫어합니다! –

5

다음은 except 문을 사용하기 때문에 Sql Server 2005 이상에서만 작동하는 다른 솔루션입니다. 그러나 나는 그것을 어떻게해서든지 공유한다. 당신은 기록 50 얻고 싶다면 - 75 쓰기 :

select * from (
    SELECT top 75 COL1, COL2 
    FROM MYTABLE order by COL3 
) as foo 
except 
select * from (
    SELECT top 50 COL1, COL2 
    FROM MYTABLE order by COL3 
) as bar 
+0

감사합니다. Microsoft SQL Server 2005에서도 작동하는 것처럼 보입니다. Microsoft SQL Server 2000에 대한 좋은 해결책은 아직 남아 있습니다.하지만 2009 년 이래로 2000 년에 대한 지원을 중단하는 것이 마침내 합리적입니다. –

+0

예, Sql Server 2005에서 작동합니다. –

+0

그건 영리한 일입니다! 나는 그런 식으로 생각하지 않았습니다. – jocull

0

거기 아주 간단합니다 나는 내 ORM에서이를 구현하려고합니다. 정말 SQL Server에 있어야하는 경우 다음 linq SQL에 의해 생성 된 코드를 다음 linq에 대한 SQL 문을 살펴보고 거기에서 갈 것입니다. 이 코드를 구현 한 MSFT 엔지니어는 수년 동안 SQL 팀의 일원이었고 그가 무엇을하고 있는지 알았습니다.

VAR 결과 = myDataContext.mytable.Skip (pageIndex * pageSize가) .Take (pageSize가)

+0

http://msdn.microsoft.com/en-us/library/bb386988.aspx : "번역은 ​​SQL Server 2000과 SQL Server 2005에서 다릅니다. Skip (Of TSource)을 any 쿼리로 사용하려는 경우 복잡성은 SQL Server 2005를 사용하십시오. " –

+0

요점은 MS SQL Server 2005를 실행하는 것이면이 문제에 대한 좋은 해결책이 많이 있다는 것입니다. 그러나 MS SQL Server 2000의 일반적인 경우에는 그 중 일부만 작동합니다. 이제는 2011 년이고 점점 더 어려워지고 있습니다. 시간이 갈수록 MS SQL Server 2000을 실행하는 것을 정당화합니다. 아직 어떤 사람들은 그렇게합니다! :( –