我不负责这个设计,但是我必须从这个类似于此的模式中提取数据(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),并且与具有特定邮政编码的地址相关联(直接或通过合同)。
问题似乎是2倍:
a)检索与合同相关的帐户
b)过滤(a)的结果以获得与某个邮政编码相关的帐户
这不起作用,因为如果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
唯一似乎有效的是UNION,但是我仍然遇到了按地址类型过滤结果的问题。
返回所需结果的最短方法是什么?目前,我倾向于创建一个临时表来存储中间数据。
答案 0 :(得分: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
重写。
您应该将所有连接条件重写为equijoins,以便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
答案 1 :(得分: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子句表现更好。