2009-06-12 4 views
3

긴 질문을 용서하십시오!데이터베이스를 이용한 쿼리를 효과적으로 수행하는 방법은 무엇입니까?

우리는 예를 들어, 두 개의 데이터베이스 테이블을 가지고 자동차와 휠. 휠은 자동차에 속하고 자동차에는 여러 개의 휠이 있다는 점에서 서로 관련이 있습니다. 그러나 차는 "버전"에 영향을 미치지 않고 변경 될 수 있습니다. 휠의 버전 (예 : 캐스케이드 업데이트 없음)에 영향을주지 않고 자동차 기록을 업데이트 할 수 있습니다 (예 : 페인트 작업). 예를 들어

는, 차 테이블은 현재 다음과 같습니다

바퀴가 테이블 (이 차가 두 바퀴가 있어요!)

WheelId, WheelVer, VersionTime, CarId 
    1   1   9:00  1 
    1   2   9:40  1 
    1   3   10:05  1 
    2   1   9:00  1 

그래서, 4 개 버전이있었습니다 다음과 같습니다

CarId, CarVer, VersionTime, Colour 
    1  1  9:00  Red 
    1  2  9:30  Blue 
    1  3  9:45  Yellow 
    1  4  10:00  Black 

이 두 륜 자동차. 첫 번째 휠 (WheelId 1)은 변경되지 않았습니다. 두 번째 바퀴는 10:05에 변경되었습니다 (예 : 페인트 칠). 필요에 따라

어떻게 효율적으로 다른 테이블에 가입 할 수 쿼리로해야합니까? 이 데이터베이스는 새로운 데이터베이스이며 스키마를 소유하고 변경하거나 감사 테이블을 추가하여이 쿼리를보다 쉽게 ​​만들 수 있습니다. CarId, CarVersion, WheelId, WheelVersion, CarVerTime, WheelVerTime과 같은 열을 사용하여 하나의 감사 테이블 접근 방식을 시도했지만 실제로 쿼리를 개선하지는 못했습니다.

예 질의 : 그것은 9시 50분의로서의 휠 기록을 포함했다으로 자동차 ID 1을 표시합니다. 당신이 다음이 시도 테이블 및 삽입을 만들려는 경우,

select c.CarId, c.VersionTime, w.WheelId,w.WheelVer,w.VersionTime,w.CarId 
from Cars c, 
( select w.WheelId,w.WheelVer,w.VersionTime,w.CarId 
    from Wheels w 
    where w.VersionTime <= "12 Jun 2009 09:50" 
    group by w.WheelId,w.CarId 
    having w.WheelVer = max(w.WheelVer) 
) w 
where c.CarId = w.CarId 
and c.CarId = 1 
and c.VersionTime <= "12 Jun 2009 09:50" 
group by c.CarId, w.WheelId,w.WheelVer,w.VersionTime,w.CarId 
having c.CarVer = max(c.CarVer) 

그리고 우리가 가지고 올 수

WheelId, WheelVer, WheelVerTime, CarId, CarVer, CarVerTime, CarColour 
    1   2   9:40  1  3  9:45  Yellow 
    2   1   9:00  1  3  9:45  Yellow 

가장 좋은 쿼리가이 :이 쿼리는이 두 행이 반환되는 결과합니다 레코드 SQL은 다음과 같습니다.

create table Wheels 
(
WheelId int not null, 
WheelVer int not null, 
VersionTime datetime not null, 
CarId int not null, 
PRIMARY KEY (WheelId,WheelVer) 
) 
go 

insert into Wheels values (1,1,'12 Jun 2009 09:00', 1) 
go 
insert into Wheels values (1,2,'12 Jun 2009 09:40', 1) 
go 
insert into Wheels values (1,3,'12 Jun 2009 10:05', 1) 
go 
insert into Wheels values (2,1,'12 Jun 2009 09:00', 1) 
go 


create table Cars 
(
CarId int not null, 
CarVer int not null, 
VersionTime datetime not null, 
colour varchar(50) not null, 
PRIMARY KEY (CarId,CarVer) 
) 
go 

insert into Cars values (1,1,'12 Jun 2009 09:00', 'Red') 
go 
insert into Cars values (1,2,'12 Jun 2009 09:30', 'Blue') 
go 
insert into Cars values (1,3,'12 Jun 2009 09:45', 'Yellow') 
go 
insert into Cars values (1,4,'12 Jun 2009 10:00', 'Black') 
go 

답변

3

이 테이블 종류는 문헌에서 유효 시간 상태 테이블이라고합니다. 각 행은 시작 날짜와 종료 날짜를 갖는 기간을 모델링해야한다는 것은 보편적으로 받아 들여집니다. 기본적으로 SQL의 작업 단위는 행이며 행은 엔티티를 완전히 정의해야합니다. 한 행에 하나의 날짜 만 있으면 쿼리가 더 복잡해질뿐만 아니라 하위 구성 요소를 다른 행으로 분할하여 설계가 손상 될 수 있습니다. 어윈 Smout, 주제에 결정적인 책 중 하나에 의해 언급

으로는 다음과 같습니다

리처드 T. 스 노드 그래스 (1999). Developing Time-Oriented Database Applications in SQL

절판되었지만 행복하게 무료 다운로드 PDF (위 링크)로 사용할 수 있습니다.

저는 실제로 그것을 읽고 많은 개념을 구현했습니다. 대부분의 텍스트는 ISO/ANSI 표준 SQL-92에 포함되어 있으며 일부는 SQL Server (다운로드 가능)를 비롯한 독점 SQL 구문으로 구현되었지만 개념 정보가 훨씬 유용하다는 사실을 알게되었습니다.

Joe Celko는 Snodgrass의 연구에서 나온 '집합에서 생각하기 : 보조, 시간 및 가상 테이블'이라는 책을 가지고 있는데, Snodgrass의 접근 방식이 두 가지로 나뉘어 진다고 말하면됩니다.

이 항목은 우리가 현재 가지고있는 SQL 제품에서 구현하기가 어렵습니다. 데이터를 일시적으로 만들기 전에 오랫동안 열심히 생각합니다. 우리가 단순히 '역사적'으로 도망 갈 수 있다면 우리는 그렇게 할 것입니다. SQL-92의 시간 기능 중 상당 부분이 SQL Server에 없습니다. INTERVAL, OVERLAPS 등이 있습니다. 기간이 겹치지 않도록하는 '기본 키'는 기본적으로 SQL Server의 CHECK 제약 조건을 사용하여 구현할 수 없으므로 트리거 및/또는 UDF가 필요합니다.

1

각 행에 시작 및 종료 시간이있을 때 조회가 쉬워집니다. 표에 종료 시간을 저장하는 것은 가장 효율적인 것,하지만이 어려운 경우, 당신은 그것을 좋아 조회 할 수 있습니다

select 
    ThisCar.CarId 
, StartTime = ThisCar.VersionTime 
, EndTime = NextCar.VersionTime 
from Cars ThisCar 
left join Cars NextCar 
    on NextCar.CarId = ThisCar.CarId 
    and ThisCar.VersionTime < NextCar.VersionTime 
left join Cars BetweenCar 
    on BetweenCar.CarId = BetweenCar.CarId 
    and ThisCar.VersionTime < BetweenCar.VersionTime 
    and BetweenCar.VersionTime < NextCar.VersionTime 
where BetweenCar.CarId is null 

당신이보기에이를 저장할 수 있습니다. 특정 날짜의 차를 선택할 수 있습니다, 뷰가 vwCars라고 말처럼 :

select * 
from vwCars 
where StartTime <= '2009-06-12 09:15' 
and ('2009-06-12 09:15' < EndTime or EndTime is null) 

당신은 저장 프로 시저를 평가 테이블이를 저장할 수 있지만 가파른 성능 저하가있을 수 있습니다.

+0

쿼리가 더 효율적 (테이블 스캔은 적음)이지만 as-of 쿼리를 수행하지 않습니다. 09:50 버전이 아닌 최신 버전 만 조회됩니다. 우리는 귀하의 질의에서 몇 가지 아이디어를 얻을 수 있습니다. 감사합니다. – ng5000

+0

쿼리의 시간 구성 요소를 쿼리에 전달해야하므로 뷰를 사용할 수 없습니다. SP는 옵션 일 수 있지만 다른 테이블에 가입해야 테이블 함수를 살펴볼 필요가 있습니다. – ng5000

+0

날짜에 대한 새로운 접근 방식으로 편집되었습니다. – Andomar

1

당신이 차 감사 테이블에 버전을 밀어 원하는 응용 프로그램에 있습니다 따라, 그 시작과 널 (NULL) 종료 날짜를 모두 가질 것이다. 높은 트래픽을받는 OLTP에서 버전 관리 방식을 사용하는 것이 상당히 비쌀 수 있으며 대부분의 읽기가 최신 버전을 가져 오는 것이 유익 할 것입니다. 당신이 시작 사이의 날짜를 찾고 보조 테이블을 조회 한 후 시작, 중지 또는 그 이상 할 수있는 시작 및 종료 날짜를 사용하여

. 각 상황에 맞는 테이블에 종료 시간을 저장

1

표현하는 쿼리가 실제로 쉽게,하지만 같은 겹칠 수 같은 차에 "아니오 두 가지 상황 (/ ... 휠)와 같은 무결성 규칙을 유지하는 문제를 야기 "(여전히 합리적으로 행할 수있는)"어떤 단일 한 (차/바퀴/...) "의 별개의 상황의 시대에 구멍이있을 수는 없다.

각 상황에 대한 테이블의 종료 시간을 저장하지 않으면 유일한 조건으로 암시 된 시간 간격으로 Allen 연산자 (겹치기, 병합, 포함)를 호출해야 할 때마다 자체 조인을 작성해야합니다. 시간 열.

SQL은 이런 종류의 임시적인 작업을 수행해야하는 경우에 그냥 악몽입니다.

덧붙여 말하자면 자연 언어로 이러한 쿼리를 정확하게 정의하는 것조차도 악몽입니다. 예를 들어, "as-of"쿼리가 필요하다고했지만, "예"10:05 (wheelVer 3) 및 10:00 (color black) 인 상황은 제외됩니다. 이러한 상황이 분명히 "현재"(as-of) 09:50이라는 사실에도 불구하고.

"임시 데이터 및 관계형 모델"을 읽는 것이 좋습니다. 책 자체가 말하듯이 "이 책은 오늘날 어디에서나 사용할 수있는 기술에 관한 것이 아닙니다"라는 이유로이 책에서 다루는 내용은 완전히 추상 적입니다.

주제에 관한 다른 표준 교과서는 Snodgrass의 것으로, 제목을 모르겠습니다. 나는이 두 책의 저자들이 해결책이 무엇인지에 관해 완전히 반대되는 태도를 취한다고 말했다.

1

이 쿼리가 반환됩니다 슬프게도이 보류 년 전 효율적이었던 것으로 :(보이지만

는 노드 그래스의 책이 SQL3에 대한 그의 작품을 기반으로

은 표준 SQL에 제안 된 확장은 임시 데이터베이스에 대한 더 나은 지원을 제공합니다 하나의 자동차 ID에 대해 똑같은 버전 시간을 가진 두 개의 행이있는 경우 중복되지만, 그 상황에서 "최신"것으로 간주하는 것을 정의하는 문제입니다. 아직 테스트 할 기회가 없지만, 그러나 나는 그것이 당신에게 필요한 것을 줄 것이라고 생각합니다. 적어도 꽤 가까웠습니다.

SELECT 
    C.car_id, 
    C.car_version, 
    C.colour, 
    C.version_time AS car_version_time, 
    W.wheel_id, 
    W.wheel_version, 
    W.version_time AS wheel_version_time, 
FROM 
    Cars C 
LEFT OUTER JOIN Cars C2 ON 
    C2.car_id = C.car_id AND 
    C2.version_time <= @as_of_time AND 
    C2.version_time > C.version_time 
LEFT OUTER JOIN Wheels W ON 
    W.car_id = C.car_id AND 
    W.version_time <= @as_of_time 
LEFT OUTER JOIN Wheels W2 ON 
    W2.car_id = C.car_id AND 
    W2.wheel_id = W.wheel_id AND 
    W2.version_time <= @as_of_time AND 
    W2.version_time > W.version_time 
WHERE 
    C.version_time <= @as_of_time AND 
    C2.car_id IS NULL AND 
    W2.wheel_id IS NULL 
+0

네이밍을 통합하여 (예 : car_id를 CarId로) 쿼리가 작동합니다. – ng5000

관련 문제