2012-11-05 106 views
5

我有两个表解析MySQL中使用逗号作为分隔符

表1与用户逗号列表分隔

Name UserID 
abc  A,B,C,D 
def  A,B,C 

表2

Name UserID 
abc A 
abc B 
abc C 
def A 
def B 

我需要找到一个在表1的用户为每个名称,但不是在table2中(当用户ID到名称对存在于table2中但不存在于CSV1中时,将不存在实例)。

输出应该

Name UserID 
abc  D 
def  C 

我可以用PHP这样做,但有没有办法这可以通过查询来完成?我不知道从哪里开始以防我作为查询执行此操作。我可以使用逗号作为分隔符在MySQL中解析吗?

+0

'我需要找到位于table1中的用户,而不是**每个名称**而不是table2。但是table1中的'D'不在**每个Name **中。 'D'只在'abc'中。那么它是如何产生你想要的输出呢? – hims056

+0

我的意思是每个名称UserID对.. – Ank

+3

你已经非规范化数据库中的数据。我建议你做的是运行一个PHP脚本来规范化数据,并将结果保存在数据库的标准化数据库中。这样,这个特定的查询和所有未来的查询将会简单得多。通常,最好避免存储在数据库字段中的逗号分隔值。有关数据库规范化的更多信息,请访问:http://databases.about.com/od/specificproducts/a/normalization.htm –

回答

3

我将测试数据插入到SQLFiddle中的测试模式中,并运行以下查询。

这里的链接到SQLFiddle与试验和积极的成果: http://sqlfiddle.com/#!2/83dfd/4/0

这里的查询:

SELECT 
COALESCE(NORMALIZED_TABLE1.NAME, TABLE2.NAME) AS NAME, 
COALESCE(NORMALIZED_TABLE1.USERID, TABLE2.USERID) AS USERID 
FROM (
    SELECT NAME, 
    SUBSTRING(
      USERID 
      FROM CASE 
       WHEN INDEX_TABLE.POS = 1 THEN 1 
       ELSE INDEX_TABLE.POS + 1 
       END 
      FOR CASE LOCATE(',', USERID, INDEX_TABLE.POS + 1) 
       WHEN 0 THEN CHARACTER_LENGTH(USERID) + 1 
       ELSE LOCATE(',', USERID, INDEX_TABLE.POS + 1) 
       END 
       - CASE 
       WHEN INDEX_TABLE.POS = 1 THEN 1 
       ELSE INDEX_TABLE.POS + 1 
       END 
    ) AS USERID 
    FROM TABLE1 
    INNER JOIN (
      SELECT @rownum:[email protected]+1 POS 
      FROM (
      SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 
      UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 
      UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 
     ) a, (
      SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 
      UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 
      UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 
     ) b, (
      SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 
      UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 
      UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 
     ) c, (SELECT @rownum:=0) r 
    ) INDEX_TABLE 
    ON INDEX_TABLE.POS <= CHAR_LENGTH(TABLE1.USERID) 
    AND (
      INDEX_TABLE.POS = 1 
      OR SUBSTRING(USERID FROM INDEX_TABLE.POS FOR 1) = ',' 
    ) 
) AS NORMALIZED_TABLE1 
LEFT OUTER JOIN TABLE2 
ON NORMALIZED_TABLE1.NAME = TABLE2.NAME 
AND NORMALIZED_TABLE1.USERID = TABLE2.USERID 
WHERE TABLE2.NAME IS NULL; 

如果table1中有很长的列宽您可能需要展开“INDEX_TABLE “子查询。你可以在这个链接上复制并粘贴代码:
http://www.experts-exchange.com/Database/MySQL/A_3573-A-MySQL-Tidbit-Quick-Numbers-Table-Generation.html

+1

完整的外连接检查两个表中缺少的数据。我刚刚意识到你只有可能从他们丢失的数据。同样的查询逻辑仍然可以工作,但是你可以在上面的代码中用右外连接 –

+0

代替完整的外连接或者......你可以用'where not exists'替换'join'语法和当前where子句...)'条款。这可能是最优雅的方式,可能会更快一些。 –

+0

由于意识到你不想从两个表中遗漏结果,所以也会发生这样的情况,它会简单得多,而且也一样。 SELECT NAME,USERID FROM TABLE2 WHERE NOT EXISTS(SELECT FROM TABLE1 WHERE TABLE1.NAME = TABLE2.NAME AND( TABLE1.USERID LIKE '%' + TABLE2.USERID + '%' 或TABLE1.USERID LIKE TABLE2.USERID +',%' OR TABLE1.USERID LIKE'%,'+ TABLE2.USERID ) ); –

0

如果你的表是固定的(你想要在这些表上工作),那么设计一个类来从它们中读取数据,而不是直接从这些预定义的表中保存设置数据;让你的对象在你的应用程序域中处理读取的数据,以获得全面的可访问性。 :-D

+0

感谢您的回复。我可以编写一个PHP脚本来解析用户列。不过,我创建了一个JavaScript/PHP模块,它将SQL查询作为输入并以有序格式显示结果。我想知道是否可以查询这个问题,我可以适应我的模块,并直接得到输出。我希望它是有道理的! – Ank

+0

:-D太注意了。 –