2013-09-27 124 views
-1

我已搜索並找不到任何幫助解決此問題的方法。該數據庫是MS SQL Server 2008 R2。即使使用MAX函數,子查詢也會返回多個行

我有一個複雜的查詢,我實際上使用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條件不好。

任何幫助表示讚賞!

+0

從合同爲V1 WHERE (V1.Description LIKE '%%金' OR V1.Description LIKE '%西爾伯%' 或V1選擇V1.Contractbegin例如'%Bronze%' 或V1.Description like'%Executive%'ORDER BY V1.Contractbegin DESC .... ....如果你運行這個,你可能會看到不止一行共享最高的V1.ContractsBegin。 – Aushin

+0

你也有一個錯字,它說「Silber」而不是「Silver」。 – Aushin

+0

選擇top n沒有order by子句似乎是尋找麻煩的好方法。 –

回答

1

你應該在哪裏使用ContractNr。您的查詢返回所有匹配的記錄max Contact Contract

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) 
+0

謝謝,我正在嘗試這個。它似乎更好地選擇最大的ContractNr。但是,Contractbegin不再有任何條件。 ContractNr條款不需要嗎?我想爲每個會員獲得系統中的最新合同,而且我應該獲得最高合約開始日期。 –

0

我總是喜歡創建一個玩具表和/或數據庫來展示我的想法。下面是一個這樣的片段。

以下是關於您的解決方案與我的解決方案的一些評論。

1 - 當您在開始時使用通配符將列與模式進行比較時,查詢優化器無法使用任何索引。因此,這會導致全表掃描。

2 - 我總是喜歡測試空值。合併是將空字符串默認爲空字符串的好方法。

3 - 如果您爲where邏輯使用持久計算列,則在插入或更新記錄時將其存儲在數據庫中。

4 - 持久計算列可以有一個索引,因此可以消除大表的表掃描。

我不得不使用查詢索引提示,因爲對於小表而言,表掃描速度更快。

此外,您可能需要添加member_fk和/或begin_date。 IE - 更多的工作/測試爲一個真實的生活的例子。

5 - 最後但並非最不重要,使用窗口分區和row_number()函數來查找最新的行。

因爲您不能在WHERE子句中的SELECT語句部分中引用計算,所以我將它綁定到CTE中。

有一些很好的概念在這裏:

  • 通配符模式搜索等於全表掃描空值

  • alwaystest /帳戶
  • 持久化計算的列的索引 速度
  • 使用分組功能進行挑選頂部結果

如果您有任何問題,只需填寫。

真誠

約翰

-- 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