-1

在这种情况下,我已经搜索并找不到任何对我有帮助的东西。数据库是 MS SQL Server 2008 R2。

我有一个复杂的查询,我实际上正在使用 CTE,其中一个子查询应该为每一列返回一行。但是,我不知道该怎么做。

这是代码。

select MemberFK as LMFK, ContractNr, Contractbegin as LC, ContractEnd as LCE from Contracts as V 
WHERE 
MainContract=1
AND Description not like '%Mitarbeiter%'
AND Description not like '%Kind%'
AND Description not like '%Teen%'
AND Description not like '%Kid%'
AND Contractbegin = 
(select TOP 1 MAX(V1.Contractbegin) from Contracts as V1 WHERE 
(V1.Description like '%Gold%'
OR V1.Description like '%Silber%'
OR V1.Description like '%Bronze%'
OR V1.Description like '%Executive%' )
AND V1.ContractEnd>V1.Contractbegin --to flush out some erroneous rows
AND V1.MemberFK =V.MemberFK) 

示例有问题的行:

LMFK    ContractNr  LC  LCE
649 644 2002-10-01 00:00:00 2008-04-30 00:00:00
755 646 2002-11-01 00:00:00 2002-11-01 00:00:00
755 647 2002-11-01 00:00:00 2008-07-31 00:00:00
754 648 2002-11-01 00:00:00 2008-07-31 00:00:00

为了每个 LMFK 只获得一行,我想做的是获得满足其他条件的最大 ContractNr。如您所见,ContractNr 646 无效,而 647 有效。看起来V1.ContractEnd>V1.Contractbegin状态不太好。

任何帮助表示赞赏!

4

2 回答 2

1

您应该在 where 语句中使用 ContractNr。您的查询返回匹配最大 Contractbegin 的所有记录

select MemberFK as LMFK, ContractNr, Contractbegin as LC, ContractEnd as LCE from Contracts as V 
WHERE 
MainContract=1
AND Description not like '%Mitarbeiter%'
AND Description not like '%Kind%'
AND Description not like '%Teen%'
AND Description not like '%Kid%'
AND ContractNr = 
(select TOP 1 MAX(V1.ContractNr) from Contracts as V1 WHERE 
(V1.Description like '%Gold%'
OR V1.Description like '%Silber%'
OR V1.Description like '%Bronze%'
OR V1.Description like '%Executive%' )
AND V1.ContractEnd>V1.Contractbegin --to flush out some erroneous rows
AND V1.MemberFK =V.MemberFK) 
于 2013-09-27T15:01:42.830 回答
0

我总是喜欢创建一个玩具表和/或数据库来炫耀我的想法。下面是一个这样的片段。

以下是关于您的解决方案与我的解决方案的一些评论。

1 - 当您在开始时使用通配符将列与模式进行比较时,查询优化器无法使用任何索引。因此,这会导致全表扫描。

2 - 我总是喜欢测试空值。合并是将空字符串默认为空字符串的好方法。

3 - 如果您在 where 逻辑中使用持久计算列,则在插入或更新记录时它会存储在数据库中。

4 - 一个持久化的计算列可以有一个索引,因此,消除了对较大表的表扫描。

我不得不使用查询索引提示,因为对于小表,表扫描更快。

此外,您可能想要添加 member_fk 和/或 begin_date。IE - 更多的工作/测试一个真实的例子。

5 - 最后但同样重要的是,使用窗口分区和 row_number() 函数来查找最新的行。

我将其捆绑到 CTE 中,因为您无法在 WHERE 子句中的语句的 SELECT 部分引用计算。

这里有一些很好的概念:

  • 通配符模式搜索等于全表扫描
  • alwaystest/account for nulls
  • 持久化计算列作为速度的索引
  • 使用分组函数来挑选最佳结果

如果您有任何问题,请大声喊叫。

真挚地

约翰

-- Drop old table
if object_id('tempdb.dbo.contracts') > 0
  drop table tempdb.dbo.contracts;
go

-- Create new table
create table tempdb.dbo.contracts
(
  id_num int identity(1,1),
  member_fk int,
  main_flag bit,
  begin_date smalldatetime,
  end_date smalldatetime,
  description_txt varchar(512),

  do_not_use_flag as
  (
    -- must have these words
    ( 
      case
        when lower(coalesce(description_txt, '')) like '%gold%' then 0
        when lower(coalesce(description_txt, '')) like '%silver%' then 0
        when lower(coalesce(description_txt, '')) like '%bronze%' then 0
        when lower(coalesce(description_txt, '')) like '%executive%' then 0
        else 1
      end
    ) 

    +

    -- must not have these words
    ( 
      case
        when lower(coalesce(description_txt, '')) like '%mitarbeiter%' then 1
        when lower(coalesce(description_txt, '')) like '%kind%' then 1
        when lower(coalesce(description_txt, '')) like '%teen%' then 1
        when lower(coalesce(description_txt, '')) like '%kid%' then 1
        else 0
      end
    ) 

    +

    -- must have begin_date <= end_date
    ( 
      case
        when begin_date is null then 1
        when end_date is null then 0
        when begin_date <= end_date then 0
        else 1
      end
    )

    + 
    (
      -- toss out non-main records
      case
        when main_flag = 1 then 0
        else 1
      end
    )
  ) persisted

);
go

-- add index on id include flag
create nonclustered index ix_contracts
    on tempdb.dbo.contracts (do_not_use_flag);
go

-- add data to table
insert into tempdb.dbo.contracts (member_fk, main_flag, begin_date, end_date, description_txt) 
values

-- shows up
(1, 1, getdate() - 5, getdate(), 'Silver - good contract for DBA'),

-- main contract <> 1
(1, 0, getdate() - 5, getdate(), 'Gold - good contract for DBA'),

-- no flag = true
(1, 1, getdate() - 5, getdate(), 'Bronze - good contract for Teen'),

-- end < begin
(1, 1, getdate(), getdate()-5, 'Bronze - good contract for DBA'),

(2, 1, getdate() - 5, getdate(), 'Executive - good contract for DBA');
go

-- wait 5 seconds
WAITFOR DELAY '00:00:02';
go

insert into tempdb.dbo.contracts (member_fk, main_flag, begin_date, end_date, description_txt) 
values
(2, 1, getdate() - 4, getdate(), 'Executive - good contract for DBA');
go


-- show the raw data
select * from tempdb.dbo.contracts as c
go

-- show the data
;
with cte_contract_by_recent_begin_dte
as
(
  select 
    ROW_NUMBER() OVER (PARTITION BY member_fk ORDER BY begin_date desc) as top_id, 
    *
  from 
    tempdb.dbo.contracts as c with(index(ix_contracts))
  where 
    c.do_not_use_flag = 0 
)
select * from cte_contract_by_recent_begin_dte as cte where cte.top_id = 1
于 2013-09-27T17:55:34.703 回答