2009-08-26 52 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

相关问题