2011-08-19 2 views
2

유사한 항목을 제거합니다. 97,98,99,120,135 열의 n + 1 열 값에서 주목할 가치가 있습니다. PK를 무시하고 공통적으로 5 개의 디지트를 가진 모든 행을 제거하려고합니다 :SQL 쿼리 내가 여섯 열이있는 테이블이 나는 SQL 서버 2008</p> <p>에 문제가 일하고

76, 89, 99, 102, 155, 122 
11, 89, 99, 102, 155, 122 
89, 99, 102, 155, 122, 130 

이 경우 알고리즘은 첫 번째 행에서 시작하고 일치하는 다섯 자리가 포함되어 있기 때문에 두 번째 및 세 번째 행을 삭제해야합니다. 첫 번째 행은 지속됩니다.

나는 무차별 적으로 솔루션을 시도했지만 첫 번째 레코드 만 모든 중복을 찾는다면 25 초 이상 걸린다는 것을 의미하므로 전체 테이블을 처리하는 데는 너무 길다 (반복적 인 프로세스 여야 함).

나는 SQL 상당히 새로운 오전하지만 내가 (나는 몇 가지 해결책을 마련했지만 아무도 ...이 최신 시도 충분하지 않았다)와 함께 온 것입니다 :

(나는 원이 ' t는 모든 코드를 포함하지만 방법을 설명 할 것이고, 도움이된다면 더 붙여 넣을 수 있습니다)

레코드 n의 자릿수를 변수에 저장하십시오. 한 자리 숫자가 FROM largeTable 인 레코드와 공통 인 모든 레코드를 선택하십시오.

선택한 모든 숫자를 #oneMatch에 삽입하고 [matchingOne]에 일치하는 숫자를 포함시킵니다.

레코드와 공통적으로 하나의 숫자가있는 모든 레코드를 선택하십시오. 임시 테이블에서 '숫자가 공통입니다'! = [matching]. 선택한 모든 자릿수를 #twoMatch에 삽입하고 [matching1] AND [matched2] 포함 ...

#fiveMatch에 삽입 할 때까지 반복하십시오. 큰 테이블에서 #fiveMatch를 삭제하고 레코드 n + 1로 이동

이 솔루션을 구현하는 데 문제가 있습니다. WHERE 절에 따라 일치하는 변수를 어떻게 할당 할 수 있습니까?

-- SELECT all records with ONE matching field: 
INSERT INTO #oneMatch (ID_pk, dOne, dTwo, dThree, dFour, dFive, dSix, mOne) 
SELECT ID_pk, dOne, dTwo, dThree, dFour, dFive, dSix 
FROM dbo.BaseCombinationsExtended 
WHERE ([dOne] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dOne? 
     OR [dTwo] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dTwo? 
     OR [dTwo] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dThree? 
... 
     OR [dSix] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dSix? 
    ) 

나는 '가짜'긴 설명을

죄송 여섯 개 쿼리하지만 너무 비효율적입니다 ... 사용하여 위의 수입니다. 어떤 도움을 주셔서 감사합니다 (새로운 솔루션이나 구현 위의 내 시도)이 문제는 저에게 잔소리가 잠시 동안 잔소리되었습니다 ...

+2

정상화를위한 좋은 사례입니다. – JNK

+0

제게 정교하게 주시겠습니까? (RDB는 나의 가장 강한 소송이 아니므로 내 무지를 용서합니다.) 단 하나의 테이블 만 있고 관계가없는 경우 어떻게 정상화 할 수 있습니까? – Cactusman

+0

동일한 행의 필드를 서로 비교하는 경우 관계가 있어야합니다. – JNK

답변

0

편집 - 다음 접근 방식 수도 있습니다 N 제곱 성능 최적화 프로그램에서. 5 개의 열 모두 색인이 생성되면 행당 6 개의 색인 탐색 만 필요하며 이는 여전히 N * logN입니다. 그것은 약간의 떡이다.

당신은 조건이 모두 5 개 경기의 순열을 기반으로 위치를 생성 코드 수 : 그래서 삭제할 레코드에 의해 주어진 것 :

SELECT * FROM SillyTable ToDelete WHERE EXISTS 
(
    SELECT PK From SillyTable Duplicate 
    WHERE ( (
      (Duplicate.dOne=ToDelete.dOne) 
      AND (Duplicate.dTwo=ToDelete.dTwo) 
      AND (Duplicate.dThree=ToDelete.dThree) 
      AND (Duplicate.dFour=ToDelete.dFour) 
      AND (Duplicate.dFive=ToDelete.dFive) 
     ) OR (
      (Duplicate.dOne=ToDelete.dTwo) 
      AND (Duplicate.dTwo=ToDelete.dThree) 
      AND (Duplicate.dThree=ToDelete.dFour) 
      AND (Duplicate.dFour=ToDelete.dFive) 
      AND (Duplicate.dFive=ToDelete.dSix) 
     ) OR (
      (Duplicate.dTwo=ToDelete.dOne) 
      AND (Duplicate.dThree=ToDelete.dTwo) 
      AND (Duplicate.dFour=ToDelete.dThree) 
      AND (Duplicate.dFive=ToDelete.dFour) 
      AND (Duplicate.dSix=ToDelete.dFive) 
     ) OR (
      (Duplicate.dTwo=ToDelete.dTwo) 
      AND (Duplicate.dThree=ToDelete.dThree) 
      AND (Duplicate.dFour=ToDelete.dFour) 
      AND (Duplicate.dFive=ToDelete.dFive) 
      AND (Duplicate.dSix=ToDelete.dSix) 
     ) ...      

이 모두 36 개 조합을 충당하기 위해 계속 (하나의 비 존재 - 조인의 각면에서 6 개의 가능한 열 중에서 일치하므로 6 * 6은 모든 가능성을 제공합니다.나는 많은 타이핑을하기 때문에 코드를 생성 할 것이고, 내일 6 시합에서 4를 원한다면 어떻게 할 것인가?

0

성능에 대한 약속은 할 수 없지만 시도해 볼 수 있습니다. 내가하는 첫 번째 일은 더 정규화 된 구조로 데이터를 넣는 것입니다.

CREATE TABLE dbo.Test_Sets_Normalized (my_id INT NOT NULL, c SMALLINT NOT NULL) 
GO 

INSERT INTO dbo.Test_Sets_Normalized (my_id, c) 
SELECT my_id, c1 FROM dbo.Test_Sets UNION ALL 
SELECT my_id, c2 FROM dbo.Test_Sets UNION ALL 
SELECT my_id, c3 FROM dbo.Test_Sets UNION ALL 
SELECT my_id, c4 FROM dbo.Test_Sets UNION ALL 
SELECT my_id, c5 FROM dbo.Test_Sets UNION ALL 
SELECT my_id, c6 FROM dbo.Test_Sets 
GO 

SELECT DISTINCT 
    T2.my_id 
FROM 
    (SELECT DISTINCT my_id FROM dbo.Test_Sets_Normalized) T1 
INNER JOIN (SELECT DISTINCT my_id FROM dbo.Test_Sets_Normalized) T2 ON T2.my_id > T1.my_id 
WHERE 
    (
    SELECT 
     COUNT(*) 
    FROM 
     dbo.Test_Sets_Normalized T3 
    INNER JOIN dbo.Test_Sets_Normalized T4 ON 
     T4.my_id = T2.my_id AND 
     T4.c = T3.c 
    WHERE 
     T3.my_id = T1.my_id) >= 5 

그러면 필요한 ID를 얻을 수 있습니다. 원하는 것을 확인한 후에는 JOIN을 원래 테이블로 되돌리고 ID로 삭제할 수 있습니다.

DISTINCT이 필요없는 어딘가의 개선이있을 수 있습니다. 나는 그것을 조금 더 생각할 것이다.

+0

DISTINCT를 요구하지 않고 dbo.Test_Sets에서 my_id를 얻을 수 있습니다. – gbn

+0

제안 사항에 대해 Tom과 psr에게 감사드립니다. 미안하지만, 즉시 (직장에서) 구현할 수는 없지만, 자유로운 순간을 얻 자마자 나는 그 (것)들에게 시도를 주며 그것이 어떻게 진행되는지 알려줄 것입니다. 다시 한번 감사드립니다. – Cactusman

+0

나는 비슷한 생각을 갖고 놀았으며, 나는 공연이 절대적으로 끔찍한 것이라고 약속 할 수 있다고 생각한다. – user12861

2

뭔가를 놓치지 않는 한 올바른 결과가 나타납니다.

declare @T table 
(
    PK INT identity primary key, 
    dOne SmallINT, 
    dTwo SmallINT, 
    dThree SmallINT, 
    dFour SmallINT, 
    dFive SmallINT, 
    dSix SmallINT 
) 

insert into @T values 
(76, 89, 99, 102, 155, 122), 
(11, 89, 99, 102, 155, 122), 
(89, 99, 102, 155, 122, 130) 

;with q1(PK, d1, d2, d3, d4, d5) as 
(
    select PK, dTwo, dThree, dFour, dFive, dSix 
    from @T 
    union all 
    select PK, dOne, dThree, dFour, dFive, dSix 
    from @T 
    union all 
    select PK, dOne, dTwo, dFour, dFive, dSix 
    from @T 
    union all 
    select PK, dOne, dTwo, dThree, dFive, dSix 
    from @T 
    union all 
    select PK, dOne, dTwo, dThree, dFour, dSix 
    from @T 
    union all 
    select PK, dOne, dTwo, dThree, dFour, dFive 
    from @T 
), 
q2 as 
(
    select PK, 
     row_number() over(partition by d1, d2, d3, d4, d5 order by PK) as rn 
    from q1 
), 
q3 as 
(
    select PK 
    from q2 
    where rn = 1 
    group by PK 
    having count(*) = 6 
) 
select T.* 
from @T as T 
    inner join q3 as Q 
    on T.PK = Q.PK 
+0

당신이 해결했습니다. 단순한 O (n log n) 문제로 변환되었습니다. 나는 그것을 보지 못했다. 잘 했어. – user12861

+0

@user - 감사합니다. 값이 왼쪽에서 오른쪽으로 증가하는 순서로 값이 정렬된다는 사실을 사용합니다. 하지만 이것이 OP 알고리즘이 실제로 원하는 알고리즘인지는 잘 모르겠습니다. 이렇게하면 삭제가 "계단식"됩니다. 이 (1,2,3,4,5,6), (1,2,3,4,5,7), (1,2,3,4,7,8) 행과 같이 세 개의 행만 있으면 우리가 먼저 행 1에 대한 모든 경기를 제거한 다음 행 2 등에 대해 제거하면 ... 행 1과 행 3이 모두 유지됩니다. 그것이 원한다면 나는 우리가 다시 O (N^2)로 돌아가는 것을 두려워합니다. –

+1

계단식 삭제에 대한 좋은 지적. 그 질문에 대한 마지막 논평에서 내가 물어 보려 던 것입니다. – user12861