使用列拆分字符串连接表的SQL查询

时间:2011-10-11 09:18:39

标签: sql sql-server split subquery

这就是我想要做的事情。

我有这些:

表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(数据,拆分器)的拆分器函数,它根据拆分器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。 我曾尝试使用公用表表达式但无法管理它。

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

2 个答案:

答案 0 :(得分:1)

确定...

当你建议你尝试CTE时,你朝着正确的方向前进。

然而,你需要做的是连锁3 CTE。一旦你有了处理链,你就需要像过滤器一样逐步传递它,首先将ID分成一列int,然后连接table2上的int来获取名称,然后重新组合这些名称。

如前所述,无论是谁设计它都设计得很糟糕,但假设您使用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服务器分割字符串的方式。您会注意到我们实际上用XML标记替换','然后使用XML处理函数使SQL服务器认为我们正在处理XML字符串。

SQL Server在2005版本中执行此类任务时有很好的例程,并允许通过制作SQL服务器将整个XML块直接序列化和反序列化到db表中的varchar字段中认为它正在处理XML,它为我们做了大部分艰苦的工作。

答案 1 :(得分:0)

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

研究这个例子,必须适用于你的情况,快速(并且不是fianl代码,例如没有对mysql失败采取措施)

把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 
?>