Разделить один столбец на множество — SQL

#sql #sql-server-2005 #select #case

#sql #sql-server-2005 #выберите #случай

Вопрос:

У меня есть столбец с именем @months, в котором хранятся месяцы в этом формате

@month = ’01-03-05-11-12′

Я хотел бы иметь запрос SELECT, который делит этот столбец на 12, делая его янв, февраль, март

Каждая моя запись имеет этот столбец впереди. Поэтому, если в записи есть @month = ’01-03′, она отображается в январе и марте. Можно ли сделать что-то подобное? Или что-нибудь близкое достаточно хорошо.

Я играл с оператором case, но не смог выдать результаты.


Код, если кто-нибудь хочет попробовать

 create table recs(
id int not null primary key,
cust_name varchar(20),
callmonth varchar(36)
)

insert into recs values(1,'john','01-12')
insert into recs values(2,'Jessica','02-06')
insert into recs values(3,'Charlie','01-06')
insert into recs values(4,'steale','03-04')
insert into recs values(5,'Silica','01-02-03-04-05-06-07-08-09-10-11-12')
insert into recs values(6,'Luder','01-03-05-07-09-11-12')
insert into recs values(7,'Panther','01-06-12')
insert into recs values(8,'Dinky','03-04-15')
  

Комментарии:

1. Почему вы храните строки с разделителями в СУБД?

2. Плохой, плохой, плохой дизайн… Исправьте структуру таблицы вместо того, чтобы блуждать в темноте, пытаясь компенсировать это неправильное дизайнерское решение.

3. вы имеете в виду, что у меня должно быть 12 разных столбцов для каждого месяца?

4. Я объясню это немного подробнее: у меня 20 тысяч записей, каждая запись имеет запланированный месяц, который будет вызываться каждый год. Существует еще одна таблица, в которой хранятся записи выполненных вызовов. До сих пор логика работает для меня, чтобы отслеживать записи за каждый месяц текущего и прошлого года? Что я делаю не так и как это исправить. И этот запрос предназначен только для меня в целях тестирования, а не для реализации в реальном коде.

Ответ №1:

Возможно, мне неясно, что вы пытаетесь сделать, но вы можете разделить на 12 таблиц, используя следующее:

 INSERT INTO January_table
SELECT *
FROM Original_table
WHERE month LIKE '%';
  

Сделайте это для каждого месяца, и это должно дать вам 12 таблиц, содержащих только значения, которые имеют этот месяц. Затем вы можете использовать представление для их объединения.

В качестве альтернативы, если вы ищете один запрос, вы можете использовать оператор case, подобный приведенному ниже:

 INSERT INTO table
SELECT *
    CASE
    WHEN month LIKE '%' THEN 'True'
    ELSE 'False'
    END,
    CASE
    WHEN month LIKE '%' THEN 'True'
    ...
FROM Original_table;
  

В результате будет получена таблица со всеми полями из исходной таблицы, за которой следуют 12 «ежемесячных столбцов», каждый из которых имеет значение true или false, указывающее, присутствует ли этот месяц в этой строке.

Комментарии:

1. О, я хотел избежать 12 запросов, поэтому я опубликовал вопрос 🙂

2. Я не уверен, что это можно сделать только с помощью 1 запроса. Или, по крайней мере, по запросу, который не является чрезвычайно сложным. Я бы рекомендовал постоянно реструктурировать таблицы так, чтобы, как вы спрашиваете, было 12 столбцов, каждый с логическим значением. Для этого может потребоваться 12 запросов, но его 12 запросов вы выполните только один раз, тогда будущая работа будет намного проще.

Ответ №2:

я согласен с другими плакатами — вам следует изменить дизайн таблицы — в том виде, в каком она у вас есть, это очень плохая форма.

ваш оператор case обычно должен иметь такую форму:

 case when instr(month,'01') > 0 then 'Jan'
  

Комментарии:

1. instr это не распознанная функция, я должен определить это сам? — Спасибо

2. Эквивалентом Oracle в MS SQL InStr является CharIndex, кстати, он все еще не работал, но синтаксис приятный 🙂

Ответ №3:

вы должны разделить значения на ‘-‘ — и тогда у вас будет таблица

и затем вы должны посмотреть, находится ли это значение внутри этой таблицы ее строковое имя.

p.s.

У вас должна быть такая таблица :

 01 - jan
02 - feb
...
...
  

допустим,
таблица месяцев (TBLMNTH) будет иметь ( id , name )

например :

 1 |   jan  
2 |   feb
select name from  TBLMNTH where @month CHARINDEX(name , @month)>-1
  

Комментарии:

1. Но я говорю о запросе. Я делаю это для некоторого анализа. Остальная часть реализации выполнена. Мне нужен просто запрос.

Ответ №4:

Создайте функцию разделения и используйте ее с именем даты, как показано ниже, вы можете получить список месяцев в строковом формате..Я думаю, что это решит часть вашей проблемы.

 select  DATENAME(month, DATEADD(month, convert(int, val) , -1 )) AS month_str 
from  SPLIT('01-03-05-11-12', '-')
  

Результат будет (протестирован на сервере mssql; он работает)

 January
March
May
November
December
  

UDF

 CREATE FUNCTION SPLIT
(
 @s nvarchar(max),
 @splitChar nchar(1)
)
returns @t table (id int identity(1,1), val nvarchar(max))
as
begin

declare @i int, @j int
select @i = 0, @j = (len(@s) - len(replace(@s,@splitChar,'')))

;with cte 
as
(
 select
  i = @i   1,
  s = @s, 
  n = substring(@s, 0, charindex(@splitChar, @s)),
  m = substring(@s, charindex(@splitChar, @s) 1, len(@s) - charindex(@splitChar, @s))

 union all

 select 
  i = cte.i   1,
  s = cte.m, 
  n = substring(cte.m, 0, charindex(@splitChar, cte.m)),
  m = substring(
   cte.m,
   charindex(@splitChar, cte.m)   1,
   len(cte.m)-charindex(@splitChar, cte.m)
 )
 from cte
 where i <= @j
)
insert into @t (val)
select pieces
from 
(
 select 
 ltrim(rtrim(case when i <= @j then n else m end)) pieces
 from cte
) t
where
 len(pieces) > 0
option (maxrecursion 0)

return

end

GO