【问题标题】:SQL query to pull unique phone numbers from 2 related tables从 2 个相关表中提取唯一电话号码的 SQL 查询
【发布时间】:2009-04-03 22:06:29
【问题描述】:

我正在尝试从 2 个单独的表创建一个 ANI 查找表,一个是商店表,另一个是这些商店的联系人列表。

我使用的是 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 where 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

这就是我得到重复条目并且插入失败的地方。

感谢您能给我的任何帮助,我即将放弃并使用光标,只是为了完成它...
戴夫

【问题讨论】:

  • 您能否添加您用于确定电话号码优先级的确切逻辑?我不确定我是否理解为什么 AltPhone 会包含在 ANI 表中。
  • 您可能希望提供两个表中的一些示例数据,然后提供您期望从解决方案查询中获得的结果。
  • ANI = 自动号码识别 RBAR = 逐行逐行
  • 不知道你的排名标准很难给出一个好的答案
  • 感谢您清理格式,Ben!

标签: sql


【解决方案1】:
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

您可以在其他查​​询中使用 distinct 的原因是您在其中使用单个 StoreID。此查询返回多个 StoreID。

【讨论】:

  • 就是这样!完美的。我之前尝试过 MIN() 函数,但语法错误。
  • 是的,SQL 对子句的顺序非常不灵活,这经常导致我编写子查询,以便我可以对选定的列进行分组(例如)。
【解决方案2】:

仅供参考,

ANI = http://en.wikipedia.org/wiki/Automatic_Number_Identification

RBAR= 逐行逐行

【讨论】:

  • 谢谢。我忘记了有些人没有听过我所拥有的每一个 TLA 和行业流行语......
【解决方案3】:

不就是简单的基于以下的查询吗:

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 可以为空,您可以添加 WHERE 子句来消除空值。我不清楚您所说的 ANI 或 RBAR 是什么意思。只要网络相同,您显然可以向不同的结果集添加额外的列。 UNION 会自动消除重复行。


如果目标表中已存在电话号码,请不要添加...

啊,那么你需要 MERGE 语句。您可以使用上述查询的一个小变体作为数据源 MERGE 到您的目标表中。

SQL 2003 标准中语句的 BNF(第 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。

不是我所知道的唯一具有该限制的 DBMS。

那么,您可能会面临创建一个临时表并使用 UNION-select 语句中的数据加载它的问题。

然后,您可以根据主数据表中相应行的不存在,从临时表中插入主数据表。至少,某些 DBMS 允许您这样做。我不是 MS SQL Server 专家,所以我不知道 UPDATE 语句的细则是否会阻止您从 UPDATE 语句的子查询中更新的表中进行选择。如果您受到限制,那可能会很麻烦。

另一个选项是将表格卸载为纯文本,并将UNION-select数据卸载为纯文本,然后使用文件系统(命令行)选项来处理它。这有多可行取决于我忘记的数据量。 Perl 在这里很有用,将主表读入散列,然后从 UNION-select 数据中选择性地更新它,最后将数据重写到加载文件。然后你“只是”开始事务,删除所有旧数据,加载所有新数据,然后交叉手指并提交。这样做的缺点是会丢失卸载和加载之间所做的更改。因此,如果您决定使用此技术,请务必小心。您可能希望在事务中进行卸载,修改数据,然后删除并重新加载——所有这些都在同一个事务中。只需按一下按钮(返回键)即可完成整个工作。

【讨论】:

  • 不起作用。当我尝试将结果集插入目标表时,由于电话号码重复而失败。在对上述 cmets 的回复中,我将尝试更好地解释我所追求的。
  • 太棒了!但是,T-SQL (MS SQL Server 2005) 不支持 MERGE INTO ...我忘了提到平台。除了一个小细节,它看起来可以解决问题。对 MERGE INTO 语法的支持将在新的 SQL Server 2008 中提供,但我们还没有。这么近!
【解决方案4】:

我看到已经选择了一个答案,但如果我没有指出一个更简单、更通用的解决方案,那我就失职了。

不要在插入顺序中隐含优先级,而是明确显示。

您的问题基本上是,“我有多个数据源,并且我知道每个源的优先级。对于每个键,我希望选择具有最高优先级的单个数据。”

首先为您的密钥(电话)选择所有可能的数据(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

我不知道你如何选择分店的电话,但有一些查询可以得到,可能是通过在 storeinfo 中使用 parentid,像这样:

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;

为什么这个解决方案更容易?因为它将优先级(隐含在您的解决方案中(由操作顺序隐含))变为我们可以选择的显式值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-20
    • 2011-10-21
    • 1970-01-01
    • 2015-12-14
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多