2014-02-22 61 views
1

我想只顯示來自「銀行」表的每個帳戶類型的最後5個交易。對於銀行表的結構是:什麼是更簡潔的方式來寫自我連接SQL

CREATE TABLE bank(
bnk_id INT(11) AUTO_INCREMENT PRIMARY KEY NOT NULL, 
...... 
bnk_acc_id INT(11) NOT NULL 
) 

我現在工作的方法是通過創建一個臨時表如下

CREATE TABLE B1 AS 
SELECT bnk_id FROM bank WHERE bnk_acc_id=1 ORDER BY bnk_date DESC LIMIT 5; 

CREATE TABLE B2 AS 
SELECT bnk_id FROM bank WHERE bnk_acc_id=2 ORDER BY bnk_date DESC LIMIT 5; 

然後我會運行下面的查詢

SELECT * 
    FROM bank 
WHERE bnk_id IN (SELECT * FROM B1) 
    OR bnk_id IN (SELECT * FROM B2) 

順便說一下,有6種不同的帳戶類型(在表中表示爲bnk_acc_id) 我會認爲有一種更有效的方式來編寫的SQL語句。請給我一個建議。

回答

3

這消除了額外的臨時表。

SELECT * FROM bank WHERE bnk_acc_id=1 ORDER BY bnk_date DESC LIMIT 5; 
UNION ALL 
SELECT * FROM bank WHERE bnk_acc_id=2 ORDER BY bnk_date DESC LIMIT 5; 
0

您需要將數據劃分到每個帳戶的行組中。然後,您可以使用此分組數據檢索每個組中的前n個數據。這意味着你不必在每個選擇中指定帳戶ID。

下面是使用臨時表來保存一些示例數據一個例子:

CREATE TABLE [#bank] 
(
    bnk_id INT IDENTITY(1, 1) PRIMARY KEY NOT NULL, 
    bnk_acc_id INT NOT NULL, 
    bnk_date datetime NOT NULL 
) 

INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES (1, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(1, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(1, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(1, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(1, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(1, getutcdate()) 

INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(2, getutcdate()) 


INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 
INSERT INTO [#bank] (bnk_acc_id, bnk_date) VALUES(3, getutcdate()) 


GO 
WITH [Grouped] AS (
SELECT bnk_id 
    , bnk_acc_id 
    , bnk_date 
    , ROW_NUMBER() 
    OVER (
     PARTITION BY [bnk_acc_id] 
     ORDER BY [bnk_date] DESC 
    ) [RowInGroup] 
FROM [#bank] 
) 

SELECT * FROM [Grouped] 
     WHERE [RowInGroup] <= 5 


DROP TABLE [#bank] 

主要部分是

WITH [Grouped] AS (
SELECT bnk_id 
    , bnk_acc_id 
    , bnk_date 
    , ROW_NUMBER() 
    OVER (
     PARTITION BY [bnk_acc_id] 
     ORDER BY [bnk_date] DESC 
    ) [RowInGroup] 
FROM [#bank] 
) 

SELECT * FROM [Grouped] 
     WHERE [RowInGroup] <= 5 

這將創建GROUPE數據,然後相應地過濾。

更多的Infor對分區在MSDN:

http://technet.microsoft.com/en-us/library/ms186734.aspx

+1

對未來的答案:如果問題用'sql'標記,則答案應該是* standard * SQL。標籤'sql'是指*查詢語言*而不是特定的DBMS產品(而不是SQL Server)。 (我指的是使用'[..]引用標識符的非標準方式 - 公共表表達式和窗口函數**是**標準SQL) –

0

根據您的數據庫上,你可能要window functions訪問。例如,在PostgreSQL裏,你可以寫這個查詢作爲

SELECT * 
FROM (
    SELECT *, row_number() OVER (PARTITION BY bnk_acc_id 
           ORDER BY bnk_date DESC) rn 
    FROM bank 
) AS b 
WHERE rn <= 5 
ORDER BY bnk_acc_id, bnk_date DESC 

這是通過指定一個行號的每一行通過相關銀行劃分和按日期排序,然後過濾下來的前5個號爲每個銀行。

從您的SQL示例中,它看起來像您使用的MySQL,可悲的是沒有窗口功能。在這種情況下,Gideon Wise的回答可能是你最好的選擇。另外,MySQL的確有user defined variables,它可以用來模擬窗口函數,如博客文章Analytic functions: FIRST_VALUE, LAST_VALUE, LEAD, LAG中所解釋的。但是,這可能對您的需求太低效,因爲您可能最終會進行全表掃描。

相關問題