如何根据字段值从几乎重复的行中选择一行?

时间:2012-04-30 21:16:12

标签: sql sql-server-2005

如果我有这些数据的行:

ID |Name        |ContractType|
---|------------|------------|
 1 |Aaron Shatz | 6-month    |
 2 |Jim Smith   |12-month    |
 3 |Jim Smith   | 6-month    |
 4 |Mark Johnson|12-month    |

我无法使用Id来确定要使用的记录:我必须使用 ContractType 。我想从表中选择所有记录,但如果有记录具有相同的 Name 值,我想选择 12个月合同记录。

查询结果应为:

ID |Name        |ContractType|
---|------------|------------|
 1 |Aaron Shatz | 6-month    |
 2 |Jim Smith   |12-month    |
 4 |Mark Johnson|12-month    |

2 个答案:

答案 0 :(得分:5)

硬编码版

此解决方案假设只有两种合约类型,即6个月和12个月。请滚动到底部以获取 dynamic 版本。

Click here to view the demo in SQL Fiddle.

脚本

CREATE TABLE contracts
(
        id              INT         NOT NULL IDENTITY
    ,   name            VARCHAR(30) NOT NULL
    ,   contracttype    VARCHAR(30) NOT NULL
);

INSERT INTO contracts (name, contracttype) VALUES
    ('Aaron Shatz',     '6-month'),
    ('Jim Smith',       '12-month'),
    ('Jim Smith',       '12-month'),
    ('Mark Johnson',    '12-month'),
    ('John Doe',        '6-month'),
    ('Mark Johnson',    '6-month'),
    ('Aaron Shatz',     '6-month');

SELECT id   
    ,   name
    ,   contracttype
FROM
(
    SELECT  id  
        ,   name
        ,   contracttype
        ,   ROW_NUMBER() OVER(PARTITION BY name ORDER BY contracttype) AS rownum
    FROM    contracts
) T1 
WHERE rownum = 1
ORDER BY id;

输出

id  name          contracttype
--  ------------  ------------
1   Aaron Shatz   6-month
2   Jim Smith     12-month
4   Mark Johnson  12-month
5   John Doe      6-month

动态版

这会将契约类型数据移动到自己的表中,并带有序列列。根据合同类型的排序方式,查询将获取相应的记录。

Click here to view the demo in SQL Fiddle.

脚本

CREATE TABLE contracts
(
        id              INT         NOT NULL IDENTITY
    ,   name            VARCHAR(30) NOT NULL
    ,   contracttypeid  INT         NOT NULL
);

CREATE TABLE contracttypes 
(
        id              INT         NOT NULL IDENTITY
    ,   contracttype    VARCHAR(30) NOT NULL
    ,   sequence        INT         NOT NULL 
)

INSERT INTO contracttypes (contracttype, sequence) VALUES
    ('12-month', 1),
    ('6-month',  3),
    ('15-month',  2);

INSERT INTO contracts (name, contracttypeid) VALUES
    ('Aaron Shatz',     2),
    ('Jim Smith',       2),
    ('Jim Smith',       3),
    ('Mark Johnson',    1),
    ('John Doe',        2),
    ('Mark Johnson',    2),
    ('Aaron Shatz',     2);

SELECT  id  
    ,   name
    ,   contracttype
FROM
(
    SELECT  c.id    
        ,   c.name
        ,   ct.contracttype
        ,   ROW_NUMBER() OVER(PARTITION BY name ORDER BY ct.sequence) AS rownum
    FROM            contracts           c
    LEFT OUTER JOIN contracttypes       ct
    ON              c.contracttypeid    = ct.id
) T1 
WHERE rownum = 1
ORDER BY id;

输出

id  name          contracttype
--  ------------  ------------
1   Aaron Shatz   6-month
3   Jim Smith     15-month
4   Mark Johnson  12-month
5   John Doe      6-month

答案 1 :(得分:4)

这仅用 ,因为OP确认只有两种合约类型是可能的,而他想要的(对于每个承包商)恰好是按字母顺序排序的那种。所以几个巧合使这个解决方案直截了当。

;WITH x AS
(
  SELECT ID, Name, ContractType, rn = ROW_NUMBER() OVER
    (PARTITION BY Name ORDER BY ContractType)
  FROM dbo.some_table
)
SELECT ID, Name, ContractType 
  FROM x
  WHERE rn = 1
  ORDER BY ID;

如果你需要让它更有活力,我想你可以说:

DECLARE @PreferredContractType VARCHAR(32);
SET @PreferredContractType = '12-month';

;WITH x AS
(
  SELECT ID, Name, ContractType, rn = ROW_NUMBER() OVER
    (PARTITION BY Name ORDER BY CASE ContractType
      WHEN @PreferredContractType THEN 1 ELSE 2 END
    )
  FROM dbo.some_table
)
SELECT ID, Name, ContractType 
  FROM x
  WHERE rn = 1
  ORDER BY ID;