2013-03-20 25 views
9

Tôi có bảng MS SQL chứa dữ liệu cổ phiếu với các cột sau: Id, Symbol, Date, Open, High, Low, Close.Cách tự tham gia bảng theo cách mọi bản ghi được kết hợp với bản ghi "trước đó"?

Tôi muốn tự tham gia bảng, vì vậy tôi có thể nhận được thay đổi hàng ngày% cho Close.

Tôi phải tạo một truy vấn sẽ tự động tham gia bảng theo cách mà mỗi bản ghi cũng chứa dữ liệu từ phiên trước đó (lưu ý rằng tôi không thể sử dụng ngày hôm qua).

Ý tưởng của tôi là để làm một cái gì đó như thế này:

select * from quotes t1 
inner join quotes t2 
on t1.symbol = t2.symbol and 
t2.date = (select max(date) from quotes where symbol = t1.symbol and date < t1.date) 

Tuy nhiên tôi không biết nếu đó là đúng/Cách nhanh nhất. Tôi nên tính đến điều gì khi suy nghĩ về hiệu suất? (Ví dụ: việc đưa chỉ mục UNIQUE vào cặp (Ký hiệu, Ngày) cải thiện hiệu suất?)

Sẽ có khoảng 100.000 bản ghi mới mỗi năm trong bảng này. Tôi đang sử dụng MS SQL Server 2008

+3

Bạn có thể hiển thị một số dữ liệu mẫu và ném nó trong một fiddle? – Kermit

+0

(1) Sử dụng phép nối trái thay vì nối bên trong, để xử lý các sản phẩm mới. Sau đó, bạn có một truy vấn chung, mà luôn luôn có thể được lọc để loại trừ các bản ghi với một NULL Right-Hand-Side. –

+1

@PieterGeerkens: Tôi chỉ quan tâm đến các hàng không có giá trị NULL, vì vậy INNER JOIN có mục đích. LEFT JOIN có cải thiện hiệu suất không? Tôi không nghĩ vậy ... –

Trả lời

0

Bạn có thể làm một cái gì đó như thế này:

DECLARE @Today DATETIME 
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP)) 

;WITH today AS 
(
    SELECT Id , 
      Symbol , 
      Date , 
      [OPEN] , 
      High , 
      LOW , 
      [CLOSE], 
      DATEADD(DAY, -1, Date) AS yesterday 
    FROM quotes 
    WHERE date = @today 
) 
SELECT * 
FROM today 
LEFT JOIN quotes yesterday ON today.Symbol = yesterday.Symbol 
    AND today.yesterday = yesterday.Date 

Bằng cách đó bạn giới hạn "hôm nay" của bạn kết quả, nếu đó là một lựa chọn.

EDIT: CTE được liệt kê là các câu hỏi khác có thể hoạt động tốt, nhưng tôi có xu hướng do dự khi sử dụng ROW_NUMBER khi xử lý 100 nghìn hàng trở lên. Nếu ngày hôm trước không phải lúc nào ngày hôm qua, tôi có xu hướng thích để kéo ra việc kiểm tra đối với ngày hôm trước trong truy vấn riêng của mình sau đó sử dụng nó để tham khảo:

DECLARE @Today DATETIME, @PreviousDay DATETIME 
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP)); 
SELECT @PreviousDay = MAX(Date) FROM quotes WHERE Date < @Today; 
WITH today AS 
(
    SELECT Id , 
      Symbol , 
      Date , 
      [OPEN] , 
      High , 
      LOW , 
      [CLOSE] 
    FROM quotes 
    WHERE date = @today 
) 
SELECT * 
FROM today 
LEFT JOIN quotes AS previousday 
    ON today.Symbol = previousday.Symbol 
    AND previousday.Date = @PreviousDay 
+0

Tôi đang mắc kẹt sử dụng năm 2005 tại thời điểm này, mà là lý do tại sao tôi có dữ liệu đó để tìm ra một ngày, có những cách đơn giản hơn trong năm 2008 và cao hơn để làm điều đó. –

+0

Điều này đang đi đúng hướng, nhưng bạn phải tính đến ngày nghỉ cuối tuần và ngày lễ. –

+0

Cuối tuần và ngày lễ sẽ yêu cầu một bảng lịch. Dễ dàng hơn nhiều để cư trú này một lần hơn để viết mã mà sẽ tính toán Phục Sinh, ví dụ, trên bất kỳ năm nào. –

9

Một lựa chọn là sử dụng một CTE đệ quy (nếu tôi đang tìm hiểu yêu cầu của bạn một cách chính xác):

WITH RNCTE AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date) rn 
     FROM quotes 
), 
CTE AS (
    SELECT symbol, date, rn, cast(0 as decimal(10,2)) perc, closed 
    FROM RNCTE 
    WHERE rn = 1 
    UNION ALL 
    SELECT r.symbol, r.date, r.rn, cast(c.closed/r.closed as decimal(10,2)) perc, r.closed 
    FROM CTE c 
    JOIN RNCTE r on c.symbol = r.symbol AND c.rn+1 = r.rn 
) 
SELECT * FROM CTE 
ORDER BY symbol, date 

SQL Fiddle Demo

Nếu bạn cần một số hoạt động cho mỗi biểu tượng để sử dụng như là phần trăm thay đổi, sau đó dễ dàng, đủ để thêm một cột bổ sung cho số tiền đó - không hoàn toàn chắc chắn ý định của bạn là gì ere, do đó, ở trên chỉ chia số tiền đóng hiện tại của số tiền đóng trước đó.

0

Điều bạn đã làm là tốt. Tôi không biết liệu việc dịch truy vấn phụ thành tham gia có giúp ích gì không. Tuy nhiên, bạn đã yêu cầu nó, vì vậy cách để làm điều đó có thể là để tham gia vào bảng một lần nữa.

select * 
from quotes t1 
inner join quotes t2 
    on t1.symbol = t2.symbol and t1.date > t2.date 
left outer join quotes t3 
    on t2.symbol = t3.symbol and t2.date > t3.date 
where t3.date is null 
+0

Bạn có thể hoặc không muốn nhóm theo các trường trong T1 để thu gọn kết quả. Vì chỉ có một kết quả của t1 và chỉ có một kết quả của t2, và vì bạn muốn các giá trị cho ngày hôm qua, có thể không. –

0

Bạn có thể sử dụng tùy chọn với CTEROW_NUMBER chức năng xếp hạng

;WITH cte AS 
(
    SELECT symbol, date, [Open], [High], [Low], [Close], 
     ROW_NUMBER() OVER(PARTITION BY symbol ORDER BY date) AS Id 
    FROM quotes 
) 
    SELECT c1.Id, c1.symbol, c1.date, c1.[Open], c1.[High], c1.[Low], c1.[Close], 
     ISNULL(c2.[Close]/c1.[Close], 0) AS perc 
    FROM cte c1 LEFT JOIN cte c2 ON c1.symbol = c2.symbol AND c1.Id = c2.Id + 1 
    ORDER BY c1.symbol, c1.date 

Đối với cải thiện hiệu suất (tránh phân loại và Lookup RID) sử dụng chỉ số này

CREATE INDEX ix_symbol$date_quotes ON quotes(symbol, date) INCLUDE([Open], [High], [Low], [Close]) 

Simple bản demo trên SQLFiddle

1

Bạn làm một cái gì đó như thế này:

with OrderedQuotes as 
(
    select 
     row_number() over(order by Symbol, Date) RowNum, 
     ID, 
     Symbol, 
     Date, 
     Open, 
     High, 
     Low, 
     Close 
     from Quotes 
) 
select 
    a.Symbol, 
    a.Date, 
    a.Open, 
    a.High, 
    a.Low, 
    a.Close, 
    a.Date PrevDate, 
    a.Open PrevOpen, 
    a.High PrevHigh, 
    a.Low PrevLow, 
    a.Close PrevClose, 

    b.Close-a.Close/a.Close PctChange 

    from OrderedQuotes a 
    join OrderedQuotes b on a.Symbol = b.Symbol and a.RowNum = b.RowNum + 1 

Nếu bạn thay đổi tham gia cuối cùng bên trái, bạn sẽ có hàng cho ngày đầu tiên cho mỗi biểu tượng, không chắc bạn có cần điều đó không.

1

Cái gì đó như công việc w'd này trong SQLite:

SELECT .. 
FROM quotes t1, quotes t2 
WHERE t1.symbol = t2.symbol 
    AND t1.date < t2.date 
GROUP BY t2.ID 
    HAVING t2.date = MIN(t2.date) 

Với SQLite là một đơn giản nhất của một loại, có lẽ trong MSSQL này cũng sẽ làm việc với những thay đổi tối thiểu.

0

Index trên (symbol, date)

SELECT * 
FROM quotes q_curr 
CROSS APPLY (
    SELECT TOP(1) * 
    FROM quotes 
    WHERE symbol = q_curr.symbol 
    AND date < q_curr.date 
    ORDER BY date DESC 
) q_prev 
Các vấn đề liên quan