2011-10-11 34 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 
?>