2011-10-11 29 views
0

以下是我正在尝试的操作。使用列分割字符串连接表的SQL查询

我有这些:

表1:

Name | Surname | Age | Location | ContactPeopleIds 
John | Cobaing | 25 | Turkey | 1234,1512,1661, 2366, 
Jack | Maltean | 29 | Italy | 6155,2333,1633, 

表2:

ID | Name | LastName | Location 
1234 | Meg | Ryan  | US 
1512 | Jesy | Jade  | US 
1661 | John | Kradel | US 
2366 | Jack | Abdona | Nigeria 

TableIWant

Name | Surname | Age | Location | ContactPeopleNames 
John | Cobaing | 25 | Turkey | Meg Ryan, Jesy Jade, John Kradel, Jack Abdona 

我发现叫fn_ParseText2Table分路器功能(数据,splitter),它使用splitter char分割数据创建一个表。 (参考here

例如:

select * 
from dbo.fn_ParseText2Table('1234,1512,1661,2366', ',') 

功能产生:

int_value | num_value | txt_value 
null  | null  | 1234 
null  | null  | 1512 
null  | null  | 1661 
null  | null  | 2366 

但是使用这个我不能创建一个查询。我不确定使用t-sql或不。 我试过使用公用表表达式,但无法管理。

如果您可以提供多种解决方案,那么提供有关其性能价值差异的详细信息会非常客气。

+0

您应该有一个联结表,而不是逗号分隔的“ContactPeopleIds”列表,然后您可以强制FK约束并消除拆分它们的需要。 –

+0

你有设计问题 http://en.wikipedia.org/wiki/First_normal_form#Repeating_groups_within_columns – pleasedontbelong

+0

是的,我知道..这不是我想要做的设计。但这是我必须处理的):任何帮助,将不胜感激。 –

回答

1

确定...

当您建议您尝试CTE时,您的方向正确。

然而,你需要做的是连锁3 CTE的在一起。一旦你有了处理链,你就需要像过滤器一样逐步传递事物,首先将ID分成一列整数,然后加入表2中的整数以获得名称,然后重新组合这些名称。

正如前面已经提到的,无论是谁设计了这个设计很糟糕,但假设您使用MS-SQL服务器和T-SQL下面的代码会做什么,你需要它:

DECLARE @tempString AS varchar(max) 
SET @tempString ='' 

;WITH firstCte AS 
(
    SELECT 
     CAST('<M>' + REPLACE(contactpeopleids, ',','</M><M>') + '</M>' AS XML) AS Names 
    FROM 
     soTable1 

    -- THIS WHERE CLAUSE MUST MATCH THE FINAL WHERE CLAUSE 
    WHERE 
     name = 'John' 
     AND surname = 'Cobaing' 
) 
,secondCte AS 
(
    SELECT 
     Split.a.value('.','VARCHAR(100)') AS NameIds 

    FROM 
     firstCte 

    CROSS APPLY Names.nodes('/M') Split(a) 
) 
,thirdCte AS 
(
    SELECT 
     t2.name + ' ' + t2.lastname AS theContactName 

    FROM 
     secondCte t1 

    -- NOTE: IF THE IDS YOU EXTRACT FROM TABLE 1 DO NOT HAVE A MATCH IN TABLE 2 YOU WILL GET NO RESULT FOR THAT ID HERE! 
    -- IF YOU WANT NULL RESULTS CHANGE THIS TO A 'LEFT JOIN'  
    INNER JOIN 
     soTable2 t2 ON t1.NameIds = t2.id 
) 
SELECT 
    @tempString = @tempString + ',' + theContactName 

FROM 
    thirdCte 

; 
-- The select substring is used to remove the leading ',' 
SELECT 
    name, 
    surname, 
    age, 
    location, 
    SUBSTRING(@tempString,2,LEN(@tempString)) AS contactpeoplenames 

FROM 
    soTable1 

WHERE 
    name = 'John' 
    AND surname = 'Cobaing' 

它可能不是尽可能优雅,为了便于使用,您可能希望将其包含在用户定义的函数中,并传递该人的姓氏和名字以查找它。如果这样做,那么您可以在常规SQL select查询中使用该函数,以将表1中的行直接返回到视图或另一个表中。

它的有趣部分实际上是我们欺骗SQL Server分裂字符串的方式。您会注意到我们实际上用XML标记替换了',',然后使用XML处理函数使SQL服务器认为我们正在处理XML字符串。

SQL Server对于执行2005版本的这种任务有很好的例程,并且允许整个XML块通过直接在db表中的varchar字段进行序列化和反序列化,方法是使SQL服务器认为它是处理XML,它为我们完成了大部分艰苦的工作。

+0

我会尝试它并回来(:谢谢。 –

+0

伟大的东西,它应该工作,我把它设置在我的MS-SQL 2K8副本中,对2个表使用与您所描述的'soTable1'和'soTable2'相同的模式所以你需要在需要的时候改变表名等。 – shawty

0
 **NORMALIZED EXAMPLE OF SELF REFERENCING ONE TO MANY RELATIONSHIP** 

研究这个例子中,必须适用于YUR情况下,使其快速(而不是fianl代码,例如采取MySQL的失败没有meassure)

是把MySQL主机用户名和密码..

<?PHP 
echo '<pre>'; 

//mysql connect 
mysql_connect('localhost', 'root',''); 
mysql_select_db("test"); 
//add some tsting data 
addTestingData(); 
//suppose this come from a user 
$_POST['user_id']=1; 

//get all contacts of user with id = 1 
$sql = 
"SELECT `tbl_users`.`user_id`, `user_name`, 
`user_surname`,`user_location` from `tbl_users` 
LEFT JOIN `tbl_user_contacts` 
ON `tbl_users`.`user_id`=`tbl_user_contacts`.`contact` 
    where `tbl_user_contacts`.`user_id`=". 
    mysql_real_escape_string($_POST['user_id'])." "; 

//get data from mysql 
$result = mysql_query($sql) ; 
while($row= mysql_fetch_row($result)) 
    print_r($row); 

///////////////end//////////////////////////////////////////// 

function addTestingData() 
{ 

mysql_query("DROP TABLE IF EXISTS `tbl_users`"); 
    mysql_query(" 
CREATE TABLE `tbl_users` (
    `user_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , 
    `user_name` VARCHAR(50) NOT NULL, 
    `user_surname` VARCHAR(50) NOT NULL, 
    `user_location` VARCHAR(50) NOT NULL, 
    `user_age` smallint not null,  
    PRIMARY KEY (`user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; 
"); 

for($i=1;$i<21;$i++) { 
     mysql_query(" 
insert into `tbl_users` (`user_name`,`user_surname`,`user_location`, 
`user_age`) values 
('name{$i}','surname{$i}', 'location{$i}', '{$i}') ") ; 
} 

mysql_query("DROP TABLE IF EXISTS `tbl_user_contacts`"); 
    mysql_query(" 
CREATE TABLE `tbl_user_contacts` (
    `user_id` MEDIUMINT UNSIGNED NOT NULL , 
    `contact` MEDIUMINT UNSIGNED NOT NULL , 
    `other_field_testing` VARCHAR(30) NOT NULL, 
    PRIMARY KEY (`user_id`,`contact`), 

    CONSTRAINT `tbl_contact_fk1` FOREIGN KEY (`user_id`) 
     REFERENCES `tbl_users` (`user_id`) 
     ON DELETE CASCADE , 

    CONSTRAINT `tbl_contact_fk2` FOREIGN KEY (`contact`) 
     REFERENCES `tbl_users` (`user_id`) 
     ON DELETE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; 
"); 

$tmp=array();//help avoid dupicate entries while testing 
for($i=1;$i<99;$i++) { 

$contact=rand(1,20); 
$user_id=rand(1,20); 

if(!in_array($contact.$user_id,$tmp)) 
{ 
    $tmp[]=$contact.$user_id; 
      mysql_query(" 
insert into `tbl_user_contacts` (`user_id`,`contact`,`other_field_testing`)  
values ('{$user_id}','{$contact}','optinal-testing') ") ; 
}//end of if 
}//end of for 
}//end of function 
?>