2014-02-22 44 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中所解释的。但是,这可能对您的需求太低效,因为您可能最终会进行全表扫描。

相关问题