2009-08-26 9 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をnull、共有値、または異なる値を持つことができます。請求または配送先住所がある場合とない場合があります。 口座エンティティは、常にまたはの配達アドレスを持ち、両方が同じである可能性があります。

私は、契約に関連付けられているすべてのアカウントを見つける必要があります(つまり、契約は同じアカウントを持ち、アカウントIDとして責任あるIDまたはIDを保持しています)、特定の郵便番号を持つ住所契約を通じて)。問題は2倍であるように思わ


A)(a)のアカウントはこれが動作しない特定の郵便番号

に関連付けられますから、結果をフィルタリング)契約 Bに関連付けられたアカウントの取得ACCOUNT_IDが問題になっている郵便番号に関連付けられていないが、holding_idは、それが返され得ることはありませんされている場合ので、: - 30を待っ

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さんがインデックス化されていないためにあまりにも遅いです分は戻ってこなかった):

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 

唯一問題だったのはUNIONですが、アドレス型で結果をフィルタリングするという問題が残っています。

必要な結果を返す最短の方法は何ですか?現時点では、私は暫定的なデータを格納する一時テーブルを作成することに傾いています。

+0

この質問に対してCREATE TABLEステートメントを単純化しているかもしれませんが、実際にはテーブルには主キーが存在しないため、JOINクエリの実行速度が遅くなる可能性があります大きな問題の)。 – MusiGenesis

+0

CREATE句とFROM句は単純化されていますが、主キーは存在しますが、テーブルが正しく索引付けされたとは限りません。残念ながら、私はこれについて何もできません。 – ilitirit

答えて

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

+0

'@ @ilitrit':どの列に索引がついていて、どの列に索引がついていないか投稿できますか? – Quassnoi

+0

索引付けされる問合せ内の唯一の列は、主キーaccount_idおよびaddress_idです。 FKは索引付けされません。 – ilitirit

関連する問題