2009-08-26 54 views
2

我不負責這個設計,但我必須從這種模式,它看起來是這樣的(SQL Server 2000中)提取數據:檢索從表中的數據,其中一個具有多個列指向在其它相同的主鍵?

CREATE TABLE contract 
(
    contract_id int, 
    account_id int,    /* account table */ 
    responsible_id int,   /* account table */ 
    holding_id int,    /* account table */ 
    billingaddress_id int,  /* address table */ 
    deliveryaddress_id int,  /* address table */ 
) 

CREATE TABLE address 
(
    address_id int, 
    postalcode char(4), 
) 

CREATE TABLE account 
(
    account_id int, 
    firstname varchar(40), 
    billingaddress_id int,  /* address table */ 
    deliveryaddress_id int,  /* address table */ 
) 

的ACCOUNT_ID,responsible_id和holding_id在合約表可以爲空,共享值或具有不同的值。它可能有也可能沒有賬單和/或送貨地址。 一個帳戶實體總是有一個計費送貨地址,都可以是相同的。

我必須找到與合同有關的所有帳戶(即合同具有相同的帳戶,負責人或持有ID作爲帳戶ID),並與具有特定郵政編碼的地址(直接或通過合同)。

的問題似乎是2倍:
一)檢索)從過濾的結果與合同 B關聯賬戶(一),以獲得具有一定郵政編碼

這不起作用關聯賬戶因爲如果ACCOUNT_ID不與有問題的郵政編碼相關,但holding_id,那麼它不會返回:

FROM account 
INNER JOIN contract 
    ON account.account_id = 
     CASE WHEN NOT IsNull(contract.account_id) THEN contract.account_id 
     WHEN NOT IsNull(contract.responsible_id) THEN contract.responsible_id 
     ELSE contract.holding_id END 

這是迄今爲止因某種原因太慢(FK的未索引 - 等待30分鐘,它未返回):

FROM account 
INNER JOIN contract 
    ON account.account_id = contract.account_id 
    OR account.account_id = contract.responsible_id 
    OR account.account_id = contract.holding_id 

似乎已經工作的唯一的事情是一個聯盟,但我仍然留下了通過地址類型篩選結果的問題。

什麼是返回所需的結果最短的方法?目前我傾向於創建一個臨時表來存儲中間數據。

+0

你可能已經簡化了這個問題你CREATE TABLE語句,但現在看來,你的表實際上沒有主鍵,這可能是負責你的連接查詢是如何執行緩慢(30分鐘有任何疑問是一個標誌的一個主要問題)。 – MusiGenesis 2009-08-26 14:00:58

+0

CREATE和FROM子句被簡化了,主鍵確實存在,但我認爲這些表沒有正確索引。不幸的是,我無能爲力。 – ilitirit 2009-08-26 14:11:39

回答

0

最後,我決定這樣的事情:

FROM (
SELECT Account.* 
FROM (SELECT Contract.Account_Id   AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Account_Id   AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Account_Id   AS ForeignKey_Id, 
       Contract.BillingAddress_Id AS Address_Id 
     FROM Contract) ContractInfo 
     JOIN Account Account 
     ON Account.Name_Id = ForeignKey_Id 
     JOIN Address 
     ON Address.Address_Id = ContractInfo.Address_Id 
      AND Address.PostalCode = 'ABCDE' 
UNION 
SELECT Account.* 
FROM (SELECT Contract.Responsible_Id  AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Responsible_Id  AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Responsible_Id  AS ForeignKey_Id, 
       Contract.BillingAddress_Id AS Address_Id 
     FROM Contract) ContractInfo 
     JOIN Account Account 
     ON Account.Name_Id = ForeignKey_Id 
     JOIN Address 
     ON Address.Address_Id = ContractInfo.Address_Id 
      AND Address.PostalCode = 'ABCDE' 
UNION 
SELECT Account.* 
FROM (SELECT Contract.Holding_Id  AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Holding_Id  AS ForeignKey_Id, 
       Contract.DeliveryAddress_Id AS Address_Id 
     FROM Contract 
     UNION 
     SELECT Contract.Holding_Id AS ForeignKey_Id, 
       Contract.BillingAddress_Id AS Address_Id 
     FROM Contract) ContractInfo 
     JOIN Account Account 
     ON Account.Name_Id = ForeignKey_Id 
     JOIN Address 
     ON Address.Address_Id = ContractInfo.Address_Id 
      AND Address.PostalCode = 'ABCDE' 
) Account 

它的性能比採用每行子查詢或IN子句更好。

3
SELECT * 
FROM contract 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM account 
     JOIN address 
     ON  address_id IN (billingaddress_id, deliveryaddress_id) 
     WHERE account_id IN (account_id, responsible_id, holding_id) 
       AND postalcode = @mycode 
     ) 

要選擇帳戶,使用此:

SELECT * 
FROM account ao 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM (
       SELECT account_id, responsible_id, holding_id 
       FROM contract c 
       WHERE c.account_id = ao.account_id 
       UNION ALL 
       SELECT account_id, responsible_id, holding_id 
       FROM contract c 
       WHERE c.responsible_id = ao.account_id 
       UNION ALL 
       SELECT account_id, responsible_id, holding_id 
       FROM contract c 
       WHERE c.holding_id = ao.account_id 
       ) co 
     JOIN account ai 
     ON  ai.account_id IN (co.account_id, co.responsible_id, co.holding_id) 
     JOIN address 
     ON  address_id IN (billingaddress_id, deliveryaddress_id) 
     WHERE postalcode = @mycode 
     ) 

更新:

既然你列沒有索引,EXISTS不會在這種情況下,有效的,因爲它不是重寫爲一個IN

你應該重寫所有的加盟條件是等值連接,使HASH JOIN方法是可用的。

試試這個:

SELECT a.account_id 
FROM (
     SELECT account_id 
     FROM contract 
     UNION 
     SELECT responsible_id 
     FROM contract 
     UNION 
     SELECT holding_id 
     FROM contract 
     ) c 
JOIN (
     SELECT account_id, billingaddress_id AS address_id 
     FROM account 
     UNION 
     SELECT account_id, deliveryaddress_id 
     FROM account 
     ) a 
ON  a.account_id = c.account_id 
JOIN address ad 
ON  ad.address_id = a.address_id 
WHERE ad.postalcode = @mycode 
+0

不錯的格式。你被錄取了。 – MusiGenesis 2009-08-26 14:03:03

+0

'@ ilitrit':你能否發佈哪些列索引,哪些不是? – Quassnoi 2009-08-26 14:19:15

+0

索引中唯一的列是主鍵account_id和address_id。 FKs未被編入索引。 – ilitirit 2009-08-26 14:26:49

相關問題