Извлечь данные из таблиц, в которых один столбец содержит несколько столбцов, указывающих на один и тот же первичный ключ в другом? - PullRequest
2 голосов
/ 26 августа 2009

Я не несу ответственности за этот дизайн, но мне нужно извлечь данные из этой схемы, которые выглядят примерно так (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, liability_id и holding_id в таблице контрактов могут иметь значение null, совместно использовать значения или иметь разные значения. Он может иметь или не иметь адрес выставления счета и / или доставки. Субъект счета всегда имеет платежный адрес доставки или , и оба могут быть одинаковыми.

Мне нужно найти все учетные записи, связанные с договорами (т. Е. Договор имеет тот же счет, ответственный или владеющий идентификатором, что и идентификатор учетной записи), и связаны с адресами, имеющими определенный почтовый индекс (либо напрямую, либо через контракт).

Проблема, кажется, в 2 раза:
а) Получение счетов, связанных с контрактами б) Фильтрация результатов из (а) для получения учетных записей, связанных с определенным почтовым индексом

Это не работает, потому что если 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, но у меня все еще остается проблема фильтрации результатов по типу адреса.

Какой самый короткий способ вернуть требуемые результаты? В данный момент я склоняюсь к созданию временной таблицы для хранения промежуточных данных.

Ответы [ 2 ]

3 голосов
/ 26 августа 2009
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 голосов
/ 28 августа 2009

В итоге я согласился на что-то вроде этого:

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...