#sql #sql-server #sql-server-2014
Вопрос:
Я использую SQL Server 2014, и у меня есть таблица (пример) , как показано ниже:
| Hosts | Description |
|------------------------------------------------------|
| 192.168.0.1,192.168.0.2,192.168.0.3 | Group A |
| 192.168.0.10,192.168.0.13,192.168.0.15 | Group B |
| 192.168.0.22 | Group C |
|------------------------------------------------------|
Я хочу, чтобы эти данные были в новой таблице:
| Hosts | Description |
|-------------------------------|
| 192.168.0.1 | Group A |
| 192.168.0.2 | Group A |
| 192.168.0.3 | Group A |
| 192.168.0.10 | Group B |
| 192.168.0.13 | Group B |
| 192.168.0.15 | Group B |
| 192.168.0.22 | Group C |
|-------------------------------|
Что я сделал:
-- List comma separated hosts in a new table (this part is working good)
set @abc = (select hosts from example for xml path(''))
set @xyz = (select replace(replace(@abc,'<HOSTS>',','),'</HOSTS>',''))
select * into newtable from split(@xyz, ',')
--My Join
select newtable.item, example.description
from newtable
left join example on newtable.item like '%' example.hosts '%'
Но я получаю:
| Hosts | Description |
|-------------------------------|
| 192.168.0.1 | NULL |
| 192.168.0.2 | NULL |
| 192.168.0.3 | NULL |
| 192.168.0.10 | NULL |
| 192.168.0.13 | NULL |
| 192.168.0.15 | NULL |
| 192.168.0.22 | Group C |
|-------------------------------|
В принципе, мое соединение работает только тогда, когда мое значение хоста находится в этой строке в одиночестве. Я ожидал, что это будет решено с помощью моего запроса «‘%’ пример.хосты ‘%'», но нет.
Я попытался использовать ту же логику, используя «перекрестное применение» и «объединение», но безуспешно.
Как вы можете видеть, у меня не так много знаний в SQL, и у меня сложилось впечатление, что мне не хватает чего-то базового, хотя я застрял в этом надолго.
Я рассчитываю на вашу помощь, спасибо!
Комментарии:
1. Почему бы не исправить свой дизайн и не использовать индивидуальный подход? Это и есть настоящее решение здесь.
2. В tsql нет функции «разделения» — так что это должно быть что-то, написанное вами или вашим коллегой. Разделение строк CSV (или любых других с использованием выбранного вами разделителя) является общей темой и имеет множество решений. В текущих версиях sql server для этого используется функция string_split () . Не изобретайте колесо заново.
3. @Larnu Не могли бы вы сообщить мне более подробно о том, как я должен это сделать, как я уже упоминал, к сожалению, мои знания ограничены. Просто чтобы ввести вас в контекст, я выполняю этот запрос через logstash, чтобы интегрировать старое и неподдерживаемое программное обеспечение в свой elasticsearch.
4. Базовый дизайн базы данных означает, что значение представляет атомарное значение @ramirkl ; это означает, что данные с разделителями не хранятся. Создайте несколько таблиц с отношениями от 1 до многих. Почитайте о нормализации. Вы должны, как минимум, стремиться к достижению 3-й нормальной формы; однако данные с разделителями нарушают первую нормальную форму.
5. @SMor забыл упомянуть, что разделение было функцией, которую я должен был добавить после другого поста здесь. Я не могу использовать string_split() в SQL 2014, как я исследовал. Но эта часть работает нормально, я могу перечислить хосты, которые были разделены запятыми.
Ответ №1:
Пожалуйста, попробуйте следующее решение.
Он использует XML и XQuery.
Примечательные моменты:
- Никаких производных таблиц или CTE.
- Раздел CData защищает от символов, таких как амперсанд и тому подобное.
- Выражение XPath содержит
text()
для максимальной производительности. Особенность SQL Server.
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, Hosts VARCHAR(1024), Description VARCHAR(30));
INSERT INTO @tbl (Hosts, Description) VALUES
('192.168.0.1,192.168.0.2,192.168.0.3', 'Group A'),
('192.168.0.10,192.168.0.13,192.168.0.15', 'Group B'),
('192.168.0.22', 'Group C');
-- DDL and sample data population, end
DECLARE @separator CHAR(1) = ',';
SELECT ID, [Description]
,x.value('text()[1]', 'VARCHAR(20)') AS Hosts
FROM @tbl
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA['
REPLACE(Hosts, @separator, ']]></r><r><![CDATA[')
']]></r></root>' AS XML)) AS t1(c)
CROSS APPLY c.nodes ('/root/r') AS t2(x);
Выход
---- ------------- --------------
| ID | Description | Hosts |
---- ------------- --------------
| 1 | Group A | 192.168.0.1 |
| 1 | Group A | 192.168.0.2 |
| 1 | Group A | 192.168.0.3 |
| 2 | Group B | 192.168.0.10 |
| 2 | Group B | 192.168.0.13 |
| 2 | Group B | 192.168.0.15 |
| 3 | Group C | 192.168.0.22 |
---- ------------- --------------
Ответ №2:
Может быть, вам следует сделать что-то вроде приведенного ниже:
SELECT Description,Split.a.value('.', 'VARCHAR(max)') 'Hosts'
FROM
(
SELECT Description, CAST ('<X>' REPLACE(Hosts, ',', '</X><X>') '</X>' AS XML) AS Data
FROM example
) AS A
CROSS APPLY Data.nodes ('/X') AS Split(a)