SQL-запрос для извлечения уникальных телефонных номеров из 2 связанных таблиц - PullRequest
2 голосов
/ 04 апреля 2009

Я пытаюсь создать таблицу поиска ANI из 2 отдельных таблиц, одна из которых представляет собой таблицу магазинов, а другая - список контактов для этих магазинов.

Я использую MS SQL Server 2005, который, к сожалению, не поддерживает синтаксис MERGE INTO ...

Хорошие вещи: Таблица поиска ANI имеет 2 значимых столбца, StoreID и PhoneNumber. Столбец PhoneNumber является (уникальным) первичным ключом, поскольку для данного PhoneNumber должен быть возвращен только один StoreID.

Store_Info значимые столбцы:

StoreID  
StorePhone  
AltPhone  

Для каждого StoreID существует одна запись с возможными дублирующимися телефонными номерами между магазинами. И да, AltPhone может быть таким же, как StorePhone ...

Store_Contacts значимые столбцы:

StoreID  
Phone  

Для StoreID имеется несколько записей и, возможно, дубликаты телефонных номеров для одного или нескольких магазинов.

Пример хранилища данных

StoreID   Parent ID StorePhone       AltPhone  
1         0         402-123-2300     402-123-2345  
2         0         202-321-7800     202-321-7890  
3         1         202-302-5600     202-302-5600  

Пример данных о контактах:

StoreID   Title    Name    Phone  
1         Mgr      Bob     402-123-2345  
1         IT       Pat     402-123-2346  
1         Reg Mgr  Dave    402-321-3213  
2         Mgr      Ann     202-231-7890  
2         IT       Mary    202-231-7893  
2         A/R      Ann     202-231-7890  
2         Reg Mgr  Dave    402-321-3213  
3         Mgr      Bob     402-123-2345  
3         AsstMgr  Pete    402-123-2356  

Я хочу вставить номера телефонов в следующем порядке:

  1. Главный / единый магазин StorePhone
  2. Главный / единый магазин AltPhone
  3. Филиал StorePhone
  4. Филиал магазина AltPhone
  5. Контактный телефон основного / единственного магазина
  6. Контактный телефон филиала
    • Если номер телефона уже существует в таблице назначения, не добавляйте его ...

Таким образом, результирующий набор данных должен быть:

StoreID  Phone  
1        402-123-2300  (first pass)  
2        202-321-7800  
1        402-123-2345  (2nd pass)  
2        202-321-7890  
3        202-302-5600  (3rd & 4th pass - only add once)  
1        402-123-2346  (5th pass - skip dup)  
1        402-321-3213  
2        202-231-7893  (do not add dups)  
3        402-123-2356  (final pass - skip dup)  

Мой подход к расстановке приоритетов по номеру телефона для дубликатов - сделать несколько запросов на основе других критериев (например, основное хранилище и ветвь), вставив первую найденную запись в таблицу поиска ANI и пропуская последующие дубликаты.

Как мне это сделать без использования RBAR? Я безуспешно попробовал следующее - на самом деле, все работает нормально, пока я не попадаю в таблицу Store_Contacts, где может быть несколько идентичных телефонных номеров для данного магазина:

INSERT INTO dbo.Store_PhoneNumbers (StoreID, PhoneNumber)
    SELECT DISTINCT StoreID, dbo.GetPhoneNumber10(StorePhone)
    FROM dbo.Store_Info
    WHERE dbo.IsAniNumber(dbo.GetPhoneNumber10(StorePhone)) = 1
        AND ParentID = 0
        AND NOT EXISTS (SELECT * FROM dbo.Store_PhoneNumbers WHERE PhoneNumber = dbo.GetPhonenumber10(StorePhone));

... повторите для AltPhone, затем StorePhone, где ParentID <> 0, затем AltPhone w / ParentID <> 0

Пока все хорошо, вот где это разваливается:

INSERT INTO dbo.Store_PhoneNumbers (StoreID, PhoneNumber)
    SELECT DISTINCT sc.StoreID, dbo.GetPhoneNumber10(sc.Phone)
    FROM Store_Contacts sc
            INNER JOIN
        Store_Info si ON sc.StoreID = si.StoreID
    WHERE (dbo.IsAniNumber(dbo.GetPhoneNumber10(sc.Phone)) = 1)
        AND (si.ParentID = 0)
        AND NOT EXISTS (SELECT * FROM dbo.Store_PhoneNumbers WHERE PhoneNumber = dbo.GetPhonenumber10(sc.Phone));

... и повторите для ParentID <> 0

Вот где я получаю дубликаты записей, и вставка не выполняется.

Спасибо за любую помощь, которую вы можете мне дать, я собираюсь сдаться и использовать курсор, просто чтобы сделать это ...
Dave

Ответы [ 4 ]

1 голос
/ 06 апреля 2009
SELECT DISTINCT sc.StoreID, dbo.GetPhoneNumber10(sc.Phone)

DISTINCT не так. Это позволит 2 магазинам использовать один и тот же номер. Используйте GROUP BY, чтобы убедиться, что второй столбец уникален.

INSERT INTO dbo.Store_PhoneNumbers (StoreID, PhoneNumber)
SELECT MIN(StoreID), PhoneNumber
FROM
(
  SELECT sc.StoreID as StoreID, dbo.GetPhoneNumber10(sc.Phone) as PhoneNumber
  FROM Store_Contacts sc
      INNER JOIN
      Store_Info si ON sc.StoreID = si.StoreID
  WHERE (dbo.IsAniNumber(dbo.GetPhoneNumber10(sc.Phone)) = 1)
      AND (si.ParentID = 0)
      AND NOT EXISTS (SELECT * FROM dbo.Store_PhoneNumbers WHERE PhoneNumber = dbo.GetPhonenumber10(sc.Phone))
) sub
GROUP BY PhoneNumber

Причиной, по которой вы могли избежать разницы в других запросах, было то, что вы работали с одним StoreID в них. Этот запрос возвращает несколько идентификаторов StoreID.

0 голосов
/ 07 апреля 2009

Я вижу, что ответ уже выбран, но я был бы упущен, если бы не указал более простое и более общее решение.

Вместо того, чтобы указывать приоритет в порядке вставки, сделайте его явным.

Ваш вопрос в основном таков: «У меня есть несколько источников данных, и я знаю приоритет для каждого. Для каждой клавиши я хочу выбрать один элемент данных с наивысшим приоритетом».

Сначала выберите все возможные данные (storeid) для своего ключа (телефона):

create table prioritized_phone( phone char(12), storeid int, priority int);

insert into prioritized_phone(phone, storeid, priority) 
select storephone, storeid, 1  from store_info
union
select altphone, storeid, 2 from store_info

Я не знаю, как вы выбираете телефон филиала магазина, но есть некоторый запрос, который получает это, вероятно, с помощью parentid в storeinfo, например:

union
select b.storephone, a.storeid, 3
from store_info a join storeinfo b on (a.parentid = b,storeid)
select b.altphone, a.storeid, 4
from store_info a join storeinfo b on (a.parentid = b,storeid)

А потом контактные телефоны:

union 
select distinct phone, storeid, 5 from storecontacts;

Как только вы это сделаете, для каждого телефона удалите любой, кроме самого низкого (лучшего) приоритета:

delete from prioritized_phone a where a.priority > 
(select min( priority) from prioritized_phone b where b.phone = a.phone);

Теперь для каждого телефона у нас есть только ряды с минимальным приоритетом. Это все еще может быть не уникальным для магазина, поэтому мы произвольно выбираем наименьший storeid для телефона с помощью:

delete from prioritized_phone a where a.store_id > 
(select min( store_id ) from prioritized_phone b where b.phone = a.phone);

Теперь у нас есть один storeid на телефон, но у нас все еще могут быть обманщики:

create table phone_lookup( phone char(12), storeid int);

insert into phone_lookup(phone, storeid)
select distinct phone, storeid 
from prioritized_phone;

Почему это решение проще? Потому что это делает приоритет, который был неявным в вашем решении (подразумеваемый по порядку операций), явным значением, которое мы можем выбрать.

0 голосов
/ 04 апреля 2009

FYI,

АНИ = http://en.wikipedia.org/wiki/Automatic_Number_Identification

RBAR = строка с агонизирующей строкой

0 голосов
/ 04 апреля 2009

Разве это не просто запрос, основанный на:

SELECT StorePhone AS Phone -- , ...other columns...
    FROM StoreInfo
UNION
SELECT AltPhone AS Phone   -- , ...other columns...
    FROM StoreInfo
UNION
SELECT Phone               -- , ...other columns...
    FROM Store_Contacts

Если AltPhone может иметь значение NULL, вы можете добавить предложение WHERE, чтобы исключить NULL. Мне не ясно, что вы имеете в виду под АНИ или RBAR. Очевидно, что вы можете добавить дополнительные столбцы к различным наборам результатов, если сеть одинакова. UNION автоматически удаляет повторяющиеся строки.


Если номер телефона уже существует в таблице назначения, не добавляйте его ...

Ах, тогда вам нужно утверждение MERGE. Вы бы MERGE в вашей целевой таблице, используя незначительный вариант запроса выше в качестве источника данных.

BNF для оператора из стандарта SQL 2003 (раздел 14.9):

<merge statement> ::=
     MERGE INTO <target table> [ [ AS ] <merge correlation name> ]
     USING <table reference> ON <search condition>
     <merge operation specification>

<merge correlation name> ::= <correlation name>

<merge operation specification> ::= <merge when clause> ...

<merge when clause> ::=
    <merge when matched clause> |
    <merge when not matched clause>

<merge when matched clause> ::=
    WHEN MATCHED THEN <merge update specification>

<merge when not matched clause> ::=
    WHEN NOT MATCHED THEN <merge insert specification>

<merge update specification> ::= UPDATE SET <set clause list>

<merge insert specification>  ::=
     INSERT [ <left paren> <insert column list> <right paren> ]
     [ <override clause> ] VALUES <merge insert value list>

<merge insert value list> ::=
     <left paren> <merge insert value element>
     [ { <comma> <merge insert value element> }... ] <right paren>

<merge insert value element> ::=
     <value expression> |
     <contextually typed value specification> 

Вы также можете найти описания этого утверждения в соответствующих руководствах по продукту, которые часто предлагают больше возможностей. В вашем случае вы, вероятно, пропустите предложение WHEN MATCHED, используя только предложение WHEN NOT MATCHED.


Дополнительное наблюдение:

MS SQL Server 2005 не поддерживает MERGE.

Не единственная СУБД, о которой я знаю, с этим ограничением.

Возможно, вы столкнулись с необходимостью создания временной таблицы и загрузки ее с данными из оператора UNION-select.

Затем вы можете выполнить вставки в вашу основную таблицу данных из временной таблицы на основании отсутствия соответствующей строки в основной таблице данных. По крайней мере, некоторые СУБД позволяют вам это делать. Я не эксперт по MS SQL Server, поэтому я не знаю, мешает ли мелкий шрифт оператора UPDATE выбрать из таблицы, обновляемой в подзапросах оператора UPDATE. Это может быть очень неприятно, если вы так ограничены.

Другой вариант - выгрузить таблицу в виде обычного текста и выгрузить данные, выбранные UNION, в обычный текст, а затем использовать опции файловой системы (командной строки) для работы с ней. Насколько это возможно, зависит от объема данных, которые я забыл. Perl может быть полезен здесь, считывая основную таблицу в хеш, а затем выборочно обновляя ее из данных, выбранных UNION, и, наконец, перезаписывая данные в файл загрузки. Затем вы просто «запускаете» транзакцию, удаляете все старые данные, загружаете все новые данные, скрещиваете пальцы и делаете коммит. Недостатком этого является то, что изменения, сделанные между разгрузкой и нагрузкой, теряются. Так что будьте осторожны, если вы решите использовать эту технику. Возможно, вы захотите выполнить выгрузку в транзакции и изменить данные, а затем удалить и перезагрузить - все в одной транзакции. Для выполнения всей работы потребуется всего одно нажатие кнопки (клавиша возврата).

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