Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
Представьте себе такую ситуацию. У вас имеется таблица (чисто для наглядности, в реальной жизни немного по другому)

CREATE TABLE dbo.example_table
(
    first_column int NOT NULL,
    second_column int NOT NULL
);


В ней храним следующие значения:

INSERT INTO @example_table(first_column, second_column)
VALUES(1, 2);
INSERT INTO @example_table(first_column, second_column)
VALUES(2, 4);
INSERT INTO @example_table(first_column, second_column)
VALUES(4, 5);


То есть, все значения first_column имеют соответствующие значения в second_column для всех записей кроме первой.

Если мы выберем все значения из таблицы (SELECT * FROM dbo.example_table)
то в результате будет 3 записи (кэп), но как можно одним запросом (только один раз получив набор всех значений SELECT * FROM dbo.example_table) из этой таблицы получить следующий результат:
1
2
4
5
То есть, суть вопроса такова, как можно "задвоить" первую или последнюю запись в таком селекте?
Я понимаю, что можно:
1) Присвоить все значения в какую-нибудь временную таблицу/табличную переменную и по row_num = 1 заинсертить 1ю запись в нее же еще раз
2) можно сформировать рекурсивный запрос, но он тут нафиг бы не нужен был :)
3) можно выполнить селект из таблицы дважды (с UNION + ROW_NUMBER OVER, взять 1ю запись во втором запросе), но в реальной жизни эта таблица имеет много записей, а выбираю я из нее 2-3 и их мне нужно увеличить до 4. Поэтому дважды заходить в таблицу не хочется (немного ресурсоемко).

Самый оптимальный вариант 1, но может быть есть возможность с помощью одной конструкции SELECT как-то хитро отловить 1ю запись и задвоить ее и потом с помощью CASE взять или first_column или second_column (мне только для своего развития интересно, так как не смог ничего придумать)?
17 май 16, 16:53    [19185038]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Akina
Member

Откуда: Зеленоград, Москва, Россия
Сообщений: 21249
А просто
SELECT first_column
FROM @example_table
UNION
SELECT second_column
FROM @example_table

чем плох?
17 май 16, 17:00    [19185079]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
Akina, да я же понимаю, что так можно (писал в п.3) но в реальной жизни в таблице несколько миллиардов записей ) и они по условиям отбираются до 3х. Хотелось бы именно обработать результат одного запроса, а не дважды заходить в таблицу (как писал в сообщении)
17 май 16, 17:02    [19185090]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Гавриленко Сергей Алексеевич
Member

Откуда:
Сообщений: 37254
KoDT
Akina, да я же понимаю, что так можно (писал в п.3) но в реальной жизни в таблице несколько миллиардов записей ) и они по условиям отбираются до 3х. Хотелось бы именно обработать результат одного запроса, а не дважды заходить в таблицу (как писал в сообщении)
В зависимости от сценариев использования, или еще одну таблицу с уникальными значениями, или индексы по обоим полям и проверить на exists там или там.
17 май 16, 17:04    [19185108]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 6802
можно и так... но я сомневаюсь, что это оптимально
SELECT 	
	[column]
FROM 
   (
	SELECT  
		first_column,
		second_column
   FROM @example_table
   ) A
UNPIVOT
   ([column] FOR X IN 
      (first_column,second_column)
)AS UNPV
GROUP BY 
	[column]
17 май 16, 17:15    [19185166]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
iljy
Member

Откуда:
Сообщений: 8711
KoDT,

with cte as
(
    ваш запрос
)
select *
from cte t join (values (1),(2)) x(x) on t.ваш критерий первой записи or x.x = 1
17 май 16, 17:16    [19185177]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
Гавриленко Сергей Алексеевич,

Да, это я понимаю (как писал выше) простые очевидные решения понятны. Самый оптимальный вариант в такой ситуации использовать local temporary table.

Но суть вопроса другая :) Всегда хочется верить, что есть интересные и новые для меня решения, которые я не смог придумать сам. Возможно можно как-то хитро использовать оконные функции, о которых я еще не слышал, и действительно получить тот результат, который я хочу. Я думал как-то хитро подключить cartesian join но что-то ничего путного не получилось.

Вопрос остается открытым:
Можно ли выполнить один селект (из любой таблицы) и 1ю строку в его результате превратить в 2 одинаковые строки?
17 май 16, 17:17    [19185184]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
TaPaK,

да, про PIVOT UNPIVOT я тоже думал... Но не очень понравилось решение.
17 май 16, 17:18    [19185191]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 6802
KoDT
TaPaK,

да, про PIVOT UNPIVOT я тоже думал... Но не очень понравилось решение.

вот прям любо дорого смотреть "не очень понравилось". Вы хоть 5 минут времени потратили на свои решения, потестировали посмотрели планы?
17 май 16, 17:19    [19185203]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Гавриленко Сергей Алексеевич
Member

Откуда:
Сообщений: 37254
Ну если у вас единственное требование -- одно "обращение" к таблице, то хоть так разворачивайте:

; with src as (
    select  
        create_date
        , modify_date
    from    sys.objects
)
select distinct
    r.date
from    src 
cross apply (
    select date = src.create_date
    union select src.modify_date
) r
17 май 16, 17:24    [19185221]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Mohoki
Member

Откуда:
Сообщений: 2
TaPaK
KoDT
TaPaK,

да, про PIVOT UNPIVOT я тоже думал... Но не очень понравилось решение.

вот прям любо дорого смотреть "не очень понравилось". Вы хоть 5 минут времени потратили на свои решения, потестировали посмотрели планы?


Ну расскажите, какой план будет у пивота при 1М записей.
17 май 16, 17:27    [19185234]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 6802
Mohoki
TaPaK
пропущено...

вот прям любо дорого смотреть "не очень понравилось". Вы хоть 5 минут времени потратили на свои решения, потестировали посмотрели планы?


Ну расскажите, какой план будет у пивота при 1М записей.

такой же как на 10
17 май 16, 17:29    [19185246]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Mohoki
Member

Откуда:
Сообщений: 2
TaPaK
Mohoki
пропущено...


Ну расскажите, какой план будет у пивота при 1М записей.

такой же как на 10


Отлично, деградация неизбежна.
17 май 16, 17:35    [19185280]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
iljy
Member

Откуда:
Сообщений: 8711
Mohoki,

PIVOT - это самая обычная группировка, и сам по себе он никакой суперкошмарной деградации не дает (Stream Group - весьма быстрый оператор). А при чем тут вообще PIVOT, когда тут явно нужен UNPIVOT, да и без него будет проще, если подумать.
17 май 16, 17:46    [19185328]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
iljy
Member

Откуда:
Сообщений: 8711
KoDT
Вопрос остается открытым:
Можно ли выполнить один селект (из любой таблицы) и 1ю строку в его результате превратить в 2 одинаковые строки?


Я вам уже написал простой и быстрый способ это сделать.
17 май 16, 17:47    [19185339]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 6802
Mohoki
TaPaK
пропущено...

такой же как на 10


Отлично, деградация неизбежна.

мда... единственное отличие 10 от 6М это вклчюние парралелизма
17 май 16, 17:48    [19185342]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 8828
KoDT,

при решении любой задачи, у которой есть зависимость решения от порядка строк требуется этот порядок строк предоставить. В Вашей постановке задачи порядок не физический, а воображаемый, так что уточните условие упорядочивания.
17 май 16, 18:01    [19185381]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
Akina
Member

Откуда: Зеленоград, Москва, Россия
Сообщений: 21249
KoDT, заходят в сортир, а не в таблицу. Хочешь побыстрее? ну так замени UNION на UNION ALL, а во второй запрос введи ещё одну копию таблицы и отбери только те записи, которые отсутствуют в первом запросе. Типа
select f1
from tab
union all
select t1.f2
from tab t1
left join tab t2
on t1.f2=t2.f1
where t2.f1 is null

Если есть подходящие индексы, получится вполне себе ничего.
17 май 16, 18:10    [19185408]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
Владислав Колосов,

Давайте приведу пример на основе реальных условий и что хочу получить.
Итак, таблица состоит из 3х полей с типом uniqueidentifier и третьим полем типа DATETIME. Фактически, это таблица с историей. Структура такая:

CREATE TABLE dbo.hist_data 
(
  old_identifier UNIQUEIDENTIFIER NOT NULL
 ,new_identifier UNIQUEIDENTIFIER NOT NULL
 ,for_date DATETIME NULL
) ON [PRIMARY]


for_date это дата, до которой была активна запись old_identifier и с которой стала активна запись new_identifier. Таким образом, чтобы мне выстроить полную историю по identifie (с какой и до какой даты была активна запись) мне нужно обработать 1ю запись дважды.

Рассмотрим пример с данными

old_identifiernew_identifierfor_date
6fb70c77-5583-11e3-b1b2-00155d004c01406b2811-7edf-11e5-89e8-00155d004c0101.01.2015
406b2811-7edf-11e5-89e8-00155d004c01dc59c05a-8dcf-11df-a054-00237d2231da01.07.2015


Считаем минимальной датой активности для первой строки "дата с" 01.01.2000 (так как ее нет в таблицах), а дату активности последней строки "дата до" NULL и реальная история активности будет такой

identifieractive_fromactive_to
6fb70c77-5583-11e3-b1b2-00155d004c0101.01.200001.01.2015
406b2811-7edf-11e5-89e8-00155d004c0101.01.201501.07.2015
dc59c05a-8dcf-11df-a054-00237d2231da01.07.2015NULL


Если я смогу 2ды получить 1ю строку то оконной функцией LAG + ROW_NUMBER с сортировкой я смогу выстроить историю.
18 май 16, 07:13    [19186728]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
sergeimv
Member

Откуда: Россия, г.Казань
Сообщений: 42
KoDT,
А почему вы сразу отвергаете ваш предложенный пункт 2?
автор
2) можно сформировать рекурсивный запрос, но он тут нафиг бы не нужен был :)

Он чем-то плох?
declare @defaultDt datetime = '20000101'

;with	
cte	as 
	(
		select
			new_identifier as id
			,for_date
		from
			dbo.hist_data
		where
			new_identifier = 'dc59c05a-8dcf-11df-a054-00237d2231da'
		union all
		select
			h.old_identifier
			,h.for_date
		from
			dbo.hist_data h
			inner join cte on id = new_identifier
	),
cte2 as 
	(
		select
			id
			,lag(for_date, 1, @defaultDt) over (order by for_date) as active_from
			,for_date as active_to
		from
			cte
	)

	select
		id
		,active_from
		,nullif(active_to, active_from) as active_to
	from
		cte2
	order by
		active_from;
18 май 16, 09:10    [19186988]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
iljy
Member

Откуда:
Сообщений: 8711
KoDT,

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

CREATE TABLE #hist_data 
(
  old int NOT NULL
 ,new int NOT NULL
 ,for_date DATETIME primary key
) ON [PRIMARY]

insert #hist_data values (1,2, '20160101'), (2,3, '20160201')

declare @t table (x int primary key)

insert @t values(1),(2)

;with cte as
(
	select *, ROW_NUMBER() over(order by for_date) rn from #hist_data
)
select *
from cte t1 full join cte t2 on t1.rn + 1 = t2.rn

drop table #hist_data


Ну если очень хочется, то можно так выбирать
;with cte as
(
	select *, ROW_NUMBER() over(order by for_date) rn from #hist_data
)
select *, ROW_NUMBER() over(order by c.for_date, x.x) rr
from cte c join @t x on c.rn = 1 or x.x = 1

, дальше делайте LAG вместо ROW_NUMBER, у меня под рукой только 2008
18 май 16, 09:28    [19187076]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
sergeimv
Member

Откуда: Россия, г.Казань
Сообщений: 42
sergeimv
KoDT,
А почему вы сразу отвергаете ваш предложенный пункт 2?
автор
2) можно сформировать рекурсивный запрос, но он тут нафиг бы не нужен был :)

Он чем-то плох?
declare @defaultDt datetime = '20000101'

;with	
cte	as 
	(
		select
			new_identifier as id
			,for_date
		from
			dbo.hist_data
		where
			new_identifier = 'dc59c05a-8dcf-11df-a054-00237d2231da'
		union all
		select
			h.old_identifier
			,h.for_date
		from
			dbo.hist_data h
			inner join cte on id = new_identifier
	),
cte2 as 
	(
		select
			id
			,lag(for_date, 1, @defaultDt) over (order by for_date) as active_from
			,for_date as active_to
		from
			cte
	)

	select
		id
		,active_from
		,nullif(active_to, active_from) as active_to
	from
		cte2
	order by
		active_from;


извиняюсь, ошибка в втором cte2
cte2 as 
	(
		select
			id
			,lead(for_date, 1, @defaultDt) over (order by for_date desc) as active_from
			,for_date as active_to
		from
			cte
	)
18 май 16, 09:28    [19187077]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
KoDT
Member

Откуда:
Сообщений: 34
iljy,

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


Я говорил что это самый простой и очевидный вариант еще в первом сообщении. Но все же я нашел другое очень интересное решение для себя без рекурсий, CTE и двойного обхода целевой таблицы :)
18 май 16, 09:43    [19187183]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 6802
KoDT
iljy,

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


Я говорил что это самый простой и очевидный вариант еще в первом сообщении. Но все же я нашел другое очень интересное решение для себя без рекурсий, CTE и двойного обхода целевой таблицы :)

прям интрига :)
18 май 16, 09:46    [19187213]     Ответить | Цитировать Сообщить модератору
 Re: Как можно использовать первую строку в результате селекта дважды  [new]
iap
Member

Откуда: Москва
Сообщений: 47145
TaPaK
KoDT
iljy,

пропущено...


Я говорил что это самый простой и очевидный вариант еще в первом сообщении. Но все же я нашел другое очень интересное решение для себя без рекурсий, CTE и двойного обхода целевой таблицы :)

прям интрига :)
Наверно, курсор...
18 май 16, 10:23    [19187462]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / Microsoft SQL Server Ответить