SQL-запрос для объединения таблиц с использованием столбцов разделенных строк - PullRequest
0 голосов
/ 11 октября 2011

Вот что я пытаюсь сделать.

У меня есть такие:

Table1:

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

Table2:

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 (data, splitter), которая создает таблицу из данных, разделенных на разделитель char. (Ссылка здесь )

Например:

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 ]

1 голос
/ 11 октября 2011

ки ...

Когда вы предложили попробовать CTE, вы идете в правильном направлении.

Что вам нужно сделать, так это цепочки 3 CTE вместе. Когда у вас есть цепочка обработки, вам нужно постепенно проходить через нее, как фильтр, сначала разбивая идентификаторы на столбцы целых, затем соединяя их в таблице 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, чтобы возвращать строки непосредственно из таблицы 1 в представление или другую таблицу.

Самое интересное в том, что мы заставляем SQL-сервер разбивать строку. Вы заметите, что мы на самом деле заменим ',' тегами XML, а затем используем функции обработки XML, чтобы сервер SQL думал, что мы обрабатываем строку XML.

В SQL Server предусмотрены отличные процедуры для выполнения задач такого рода, например, в версии 2005 года, и он позволяет сериализовать и десериализовать целые блоки XML в поле varchar прямо из таблицы db, создав SQL-сервер. думаю, что он имеет дело с XML, он делает большую часть тяжелой работы для нас.

0 голосов
/ 11 октября 2011
      **NORMALIZED EXAMPLE OF SELF REFERENCING ONE TO MANY RELATIONSHIP**

Изучите этот пример, примените его к вашему случаю, сделайте его быстрым (и это не код фианлы, например, никакие меры не принимаются при сбое 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 
?>
...