Nice programing

SQL IN은 성능에 좋지 않습니까?

nicepro 2020. 12. 3. 19:41
반응형

SQL IN은 성능에 좋지 않습니까?


다음과 같은 쿼리가 있습니다.

SELECT FieldX, FieldY FROM A
WHERE FieldW IN (108, 109, 113, 138, 146, 160,
307, 314, 370, 371, 441, 454 ,457, 458, 479, 480,
485, 488, 490, 492, 519, 523, 525, 534, 539, 543,
546, 547, 550, 564, 573, 629, 642, 643, 649, 650,
651, 694, 698, 699, 761, 762, 768, 772, 773, 774,
775, 778, 784, 843, 844, 848, 851, 852, 853, 854,
855, 856, 857, 858, 859, 860, 861, 862, 863, 864,
865, 868, 869, 871, 872, 873, 891) 

옵션이 너무 많은 IN 절이 있으면 쿼리 성능에 좋지 않습니까? 내 응용 프로그램에서 많은 시간 초과가 발생했으며 이것이 이러한 종류의 문제의 원인 일 수 있다고 생각합니다. 좋은 SQL 힌트를 사용하여 숫자를 제거하지 않고 쿼리를 최적화 할 수 있습니까?

편집하다:

@KM 이들은 다른 테이블의 키입니다. 이것은 간단히 설명하는 포럼 애플리케이션입니다. C #은 데이터베이스에서 모든 포럼을 가져와 앱 캐시에 저장합니다. C #이 이러한 포럼과이 사용자에 대한 스레드를 가져 오는 프로 시저를 호출하기 전에 C #은 권한 및 일부 비즈니스 논리를 고려하여 "모든 포럼"컬렉션을 필터링하는 일부 논리를 수행합니다. 시간 초과는 애플리케이션 자체가 아니라 데이터베이스에서 발생합니다. 쿼리에서이 모든 논리를 수행하려면 많은 내부 조인이 필요하며 프로 시저 내에서이 모든 작업을 수행 할 수 있는지 100 % 확신 할 수 없습니다.

내가 사용하고 2000 SQL 서버를


IN 연산자를 사용하여 쿼리를 작성할 때 성능에 영향을 줄 수있는 몇 가지 고려 사항이 있습니다.

첫째, IN 절은 일반적으로 OR 논리적 연결을 사용하기 위해 대부분의 데이터베이스에서 내부적으로 다시 작성됩니다. 따라서 다음 col IN ('a','b','c')으로 다시 작성되었습니다 (COL = 'a') OR (COL = 'b') or (COL = 'c'). 두 쿼리에 대한 실행 계획 할 가능성이 당신이 인덱스를 가지고 있다고 가정 동등 col.

둘째, IN 또는 OR를 가변 개수의 인수와 함께 사용하면 데이터베이스에서 인수가 변경 될 때마다 쿼리를 다시 구문 분석하고 실행 계획을 다시 작성해야합니다. 쿼리 실행 계획을 작성하는 것은 비용이 많이 드는 단계 일 수 있습니다. 대부분의 데이터베이스는 EXACT 쿼리 텍스트를 키로 사용하여 실행하는 쿼리에 대한 실행 계획을 캐시합니다. 비슷한 쿼리를 실행하지만 조건 자에 다른 인수 값을 사용하면 데이터베이스가 실행 계획을 구문 분석하고 구축하는 데 상당한 시간을 소비하게됩니다. 이것이 최적의 쿼리 성능을 보장하는 방법으로 바인드 변수를 강력히 권장하는 이유 입니다.

셋째, 많은 데이터베이스는 실행할 수있는 쿼리의 복잡성에 제한이 있습니다. 이러한 제한 중 하나는 술어에 포함될 수있는 논리적 연결의 수입니다. 귀하의 경우 수십 개의 값이 데이터베이스의 기본 제공 한계에 도달 할 가능성이 낮지 만 IN 절에 수백 또는 수천 개의 값을 전달할 것으로 예상되는 경우 확실히 발생할 수 있습니다. 이 경우 데이터베이스는 단순히 쿼리 요청을 취소합니다.

넷째, 조건 자에 IN 및 OR을 포함하는 쿼리는 항상 병렬 환경에서 최적으로 다시 작성 될 수 없습니다. 병렬 서버 최적화가 적용되지 않는 다양한 경우가 있습니다. MSDN에는 병렬 처리를위한 쿼리 최적화에 대한 적절한 소개 가 있습니다. 일반적으로 UNION ALL 연산자를 사용하는 쿼리는 대부분의 데이터베이스에서 사소하게 병렬화 가능하며 가능하면 논리 연결 (예 : OR 및 IN)보다 선호됩니다.


FieldW에 대한 좋은 색인이 있다면 그 IN을 사용하는 것이 완벽합니다.

방금 테스트했으며 SQL 2000은 IN을 사용할 때 클러스터형 인덱스 스캔을 수행합니다.


임시 테이블을 만들고 여기에 값을 삽입 한 다음 IN술어 에서 대신 테이블을 사용할 수 있습니다.

AFAIK SQL Server 2000는 상수 집합의 해시 테이블을 만들 수 없으므로 최적화 프로그램에서 HASH SEMI JOIN.

이것은 당신이 (당신이 가지고 있어야하는) 색인이없는 경우에만 도움이 될 것 FieldW입니다.

인덱스에 FieldXFieldY을 포함 할 수도 있습니다 .

CREATE INDEX ix_a_wxy ON a (FieldW, FieldX, FieldY)

색인을 사용해야 만 쿼리를 제공 할 수 있습니다.

SQL Server 2000INCLUDE옵션 이 부족 CREATE INDEX하여 DML성능이 약간 저하 될 수 있지만 쿼리 성능이 향상됩니다.

최신 정보:

실행 계획에서 복합 색인이 필요한 것보다 (SettingsID, SectionID)

SQL Server 2000실제로 상수 목록에서 해시 테이블을 만들 수 있지만 수행 Hash Semi Join할 수 있지만 대부분 Nested Loop쿼리 쿼리 보다 효율성이 떨어집니다 .

참고 사항 : WHERE조건을 충족하는 행 수를 알아야하는 경우를 COUNT(column)사용 COUNT(*)하지 말고 대신 사용하십시오.

A COUNT(column)column값이 인 행을 계산하지 않습니다 NULL.

먼저, 당신은 두 번째, 당신이 기대하지 않은 결과를 얻을 수 있으며이 수단은 최적화가 추가로 수행해야합니다 Key Lookup/를 Bookmark Lookup당신의 열이 역할을 인덱스에 포함되지 않은 경우 WHERE조건을.

ThreadId것처럼 보이기 CLUSTERED PRIMARY KEY때문에이 쿼리에는 괜찮지 만 일반적으로 피하십시오.


데이터 분배에 따라 WHERE 절의 추가 술어가 성능을 향상시킬 수 있습니다. 예를 들어, ID 세트가 테이블의 총 수에 비해 작고 ID가 비교적 가깝다는 것을 알고있는 경우 (아마도 일반적으로 최근에 추가 된 것이므로 범위의 높은 끝에서 클러스터링 됨), 조건 자 "AND FieldW BETWEEN 109 AND 891"을 포함 할 수 있습니다 (C # 코드에서 세트의 최소 및 최대 ID를 결정한 후). 해당 열 (인덱싱 된 경우)에서 범위 스캔을 수행하면 현재 사용중인 것보다 더 빠르게 작동 할 수 있습니다.


코딩하는 더 좋은 방법이 있지만, 특히 SELECT 만있는 경우 시간 초과의 원인이 아닌 것 같습니다. 하지만 쿼리 추적을보고이를 확인할 수 있어야합니다. 그러나 이것을 기록하는 것은 추측에 의한 최적화이며 그럴 가능성은 희박합니다.

실제로 시간이 초과되는 쿼리에 대한 쿼리 계획부터 시작하겠습니다. 어떤 쿼리인지 알고 있습니까?


IN은 큰 OR 목록을 작성하는 것과 똑같습니다. OR는 종종 쿼리를 SARG 불가능하게 만들므로 인덱스가 무시 될 수 있으며 계획은 전체 스캔을 수행합니다.


일반적으로 IN 절은 성능에 해롭지 만 "나쁜"것은 애플리케이션, 데이터, 데이터베이스 크기 등에 따라 다릅니다. 어떤 것이 가장 좋은지 확인하려면 자체 앱을 테스트해야합니다.


Basically what that where clause does is "FieldW = 108 OR FieldW = 109 OR FieldW = 113...". Sometimes you can get better performance by doing multiple selects, and combining them with union. For example:

SELECT FieldX, FieldY FROM A WHERE FieldW = 108
UNION ALL
SELECT FieldX, FieldY FROM A WHERE FieldW = 109

But of course that is impractical when you're comparing to so many values.

Another option might be to insert those values into a temporary table and then joining the A table to that temp table.


the size of your table will determine the speed when using this statement. If it's not a very large table...this statement isn't affecting your performance.


Here is your answer...

http://www.4guysfromrolla.com/webtech/031004-1.shtml

Basically, you want to create a function that will split a string and populate a temp table with the split contents. Then you can join to that temp table and manipulate your data. The above explains things pretty well. I use this technique a lot.

In your specific case use a join to the temp table instead of an in clause, much faster.


I generally would use a user-defined table type for queries like this.

CREATE TYPE [dbo].[udt_int] AS TABLE (
    [id] [int] NOT NULL
)

Using a table-variable and filling it with rows for each of your numbers, you can do:

SELECT 
    FieldX, 
    FieldY
FROM A
INNER JOIN @myIds B ON
    A.FieldW = B.id

Performance can only be judged in the context of what you are trying to do. In this case you are requesting the retrieval of around 70 rows (assuming thay are unique values), so you can expect something like 70 times the duration of retrieving a single value. It might be less due to caching, or course.

However, the query optimiser may need or choose to perform a full table scan in order to retrieve the values, in which case performace will be little different than retrieving a single value via the same access plan.


If you can use other things than IN : do it (I was using IN in some case not really the good way : I can easily replace with exist and it is quicker)

In your case : It seems not so bad.


You might try something like:

select a.FieldX, a.FieldY
from (
    select FieldW = 108 union
    select FieldW = 109 union
    select FieldW = 113 union
    ...
    select FieldW = 891
) _a
join A a on a.FieldW = _a.FieldW

It may be appropriate for your situation, such as when you want to generate a single SQL statement dynamically. On my machine (SQL Server 2008 Express), testing with a small number (5) of FieldW values and a large number (100,000) of rows in A, this uses an index seek on A with a nested loops join between A and _a, which is probably what you're looking for.

참고URL : https://stackoverflow.com/questions/1013797/is-sql-in-bad-for-performance

반응형