SQL查询 - 一列必须是不同的,受限列必须是最新的

时间:2010-02-11 06:43:58

标签: sql distinct sorted

我有一个CATEGORIES表,链接到ITEMS表(一对多)。每个项目表可以在BARCODES表中有多个条形码(一对多)。

这个想法是可以在某个时刻针对某个项目添加新的条形码,但是旧的数据存储在BARCODES表中,这样如果使用去年的数据进行搜索,则项目和类别旧的条形码仍然可以很快找到。

希望这是有道理的 - 这有点过于简单了。

我的表格示例:

CATEGORIES TABLE
ID   NAME        CODE    ACCOUNTING_REFERENCE
1    Beverages   BEV     Stock_Beverages
2    Pies        PIE     Stock_Pies
3    Chips       CHP     Stock_Chips

ITEMS TABLE
ID   CATEGORY_ID  DESCRIPTION                 BASE_COST
1    1            Red Bull (single)           $4.50
2    2            Ponsonby Pie - Mince Cheese $2.99
3    1            Coke Can (single)           $3.50
4    2            Big Ben - Steak Pepper      $1.99

BARCODES TABLE
ID  ITEM_ID  BARCODE   ACTIVE_FROM
1   1        XSD123    2009/10/11
2   2        AXF123    2009/10/12
3   3        XYZ234    2009/10/11
4   1        NEW001    2010/01/05
5   1        NEW002    2010/01/05*
  • 我知道在这种情况下,在同一天输入两个条形码是没有意义的。忽略这看起来像坏数据。这个例子只是我正在做的事情的类比 - 在反映这种结构的真实世界应用程序中,在同一天输入两个“BARCODES”是有意义的。我想公开讨论真实数据,但我不能自由地这样做 - 如果它令人困惑,我很抱歉。

我正在尝试向用户显示商品信息。对于所有项目,我想从每个项目的SQL 一个记录返回。返回的信息应包括行的类别名称,代码和会计引用 - 这些列的副本很好。查询需要返回项目描述和基本成本。

我能做的很多。

我还希望查询返回该项目的最新条形码。在同一天为某个项目输入两个条形码的情况下,显示哪一个条形码并不是非常重要 - 尽管显示两个(或更多)BARCODE.ID字段中较高的一个将是一个很好的接触。 / p>

此外,项目可能不存在任何条形码,在这种情况下我想返回一个空条目。

我想要的结果集看起来像这样:

ITEM_ID   DESCRIPTION                  BASE_COST  CATEGORY_NAME CATEGORY_CODE  ACCOUNTING_REFERENCE  BARCODE
1         Red Bull (single)            $4.50      Beverages     BEV            Stock_Beverages       NEW002
2         Ponsonby Pie - Mince Cheese  $2.99      Pies          PIE            Stock_Pies            AXF123
3         Coke Can (single)            $3.50      Beverages     BEV            Stock_Beverages       XYZ234
4         Big Ben - Steak Pepper       $1.99      Pies          PIE            Stock_Pies            <null>

根据上面的表格,我无法弄清楚如何将BARCODE列添加到此结果集中。

如果我还不清楚:我需要知道如何构建一个SQL查询,根据我提供的数据,它将给出我上面的结果。

要求我的结果集中的ITEM_ID列是不同的。我不能只限制内存中的陈旧条目,如果我冒出多个ITEM_ID,它会破坏我正在修改的应用程序中其他地方使用的PK / FK关系。

为了记录,我已经浏览了网站上与不同sql列相关的任何内容来回答这个问题。我发现了很多条目,但似乎无法得到任何建议的解决方案对我有用。也许我只是密集 - 它来得太晚了,我可能不会直接思考。如果我错过了一些明显的东西,请道歉。

修改

Gabe在下面给出了一个很好的答案,我意识到我应该更清楚。

Gabe的答案对我不起作用,因为偶尔在我的数据中我有两个条形码对同一个具有相同时间戳的项目。当我尝试他的代码时,每当一个项目在同一天有两个条形码放在它上面时,我就会返回多个项目。

这实际上是违反直觉的,所以没有正确沟通是我的错。基本上,上面描述的场景实际上并不是我正在使用的数据。我不能自由讨论我正在公开工作的数据库,因此我必须重命名我使用的示例中的所有内容。

我已经调整了上面的问题。我知道使用相同的时间戳输入两个条形码似乎很荒谬 - 请放心,这可能会发生在实际的数据库中。

编辑(再次)

Gabe和 simon 的答案都有效。我之所以选择Gabe作为答案,只是因为我发现他的陈述最清晰。

也就是说,我也喜欢 simon ,因为它显示了使用我不熟悉的SQL的语法 - 很高兴看到一个不熟悉的语法。

在某些时候,我需要对两种方法进行基准测试,以确定哪种方法更快。使用我的示例数据,它们目前是相同的 - 虽然我希望我可以通过一些工作来填充我的数据库,增加几千条记录并修改我的索引。

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

听起来你想要一个相关的子查询。粗略地说,这个:

 select *
from ITEMS
join BARCODES as B on ITEMS.ID = B.ITEM_ID
join CATEGORIES on CATEGORIES.ID = ITEMS.CATEGORY_ID
where B.ACTIVE_FROM =
    (select max(ACTIVE_FROM)
     from BARCODES as B2
     where B2.ITEM_ID = B.ITEM_ID)

要处理给定项目没有条形码的情况,您需要一个外部联接,并且只有一个条形码需要一个额外的子查询。在ANSI SQL中,它可能如下所示:

 select *
from ITEMS
LEFT OUTER join BARCODES as B on ITEMS.ID = B.ITEM_ID
join CATEGORIES on CATEGORIES.ID = ITEMS.CATEGORY_ID
where B.ACTIVE_FROM =
    (select max(ACTIVE_FROM)
     from BARCODES as B2
     where B2.ITEM_ID = B.ITEM_ID)
    AND B.ID =
    (SELECT MAX(ID)
     FROM BARCODES AS B3
     WHERE B3.ITEM_ID = B.ITEM_ID)
    OR ACTIVE_FROM IS NULL

答案 1 :(得分:1)

尝试这样的事情:

use tempdb
go
if exists (select 1 from sys.objects where name = 'barcodes')
    drop table barcodes
if exists (select 1 from sys.objects where name = 'items')
    drop table items
if exists (select 1 from sys.objects where name = 'categories')
    drop table categories
go
create table categories (
    id int primary key,
    [name] nvarchar(30),
    code char(3),
    accounting_reference nvarchar(30)
)
create table items (
    id int primary key,
    category_id int foreign key references categories (id),
    description nvarchar(50),
    base_cost money
)
create table barcodes (
    id int primary key,
    item_id int foreign key references items (id),
    barcode varchar(10),
    active_from datetime
)
go
insert into categories (id, [name], code, accounting_reference)
select 1, 'Beverages', 'BEV', 'Stock_Beverages' union all
select 2, 'Pies', 'PIE', 'Stock_Pies' union all
select 3, 'Chips', 'CHP', 'Stock_Chips'

insert into items (id, category_id, description, base_cost)
select 1, 1, 'Red Bull (single)', 4.5 union all
select 2, 2, 'Ponsonby Pie - Mince Cheese', 2.99 union all
select 3, 1, 'Coke Can (single)', 3.50 union all
select 4, 2, 'Big Ben - Steak Pepper', 1.99

insert into barcodes (id, item_id, barcode, active_from)
select 1, 1, 'XSD123', '2009/10/11' union all
select 2, 2, 'AXF123', '2009/10/12' union all
select 3, 3, 'XYZ234', '2009/10/11' union all
select 4, 1, 'NEW001', '2010/01/05' union all
select 5, 1, 'NEW002', '2010/01/05'

;with x as (
    select item_id, max(active_from) active_from, max(id) id
    from barcodes
    group by item_id
),
y as (
    select item_id, barcode
    from barcodes
    where exists (select 1 from x where item_id = barcodes.item_id and id = barcodes.id and active_from = barcodes.active_from)
)
select t1.id item_id, t1.description, t1.base_cost, t2.name category_name, t2.code category_code, t2.accounting_reference, t3.barcode
from items t1 left join categories t2 on (t1.category_id = t2.id)
    left join y t3 on (t1.id = t3.item_id)