|
|
Microsoft SQL Server
 Скрипты
 T-SQL
 Хранимая процедура crosstab - аналог TRANSFORM MS ACCESS |
 |
|
Автор: Shurgenz
Прислал: Shurgenz
| |
Актуальной в последнее время становится задача преобразования разработанных ранее mdb приложений MS ACCESS в ADP проекты. Одна из сложностей такого преобразования состоит в том, что не все инструкции языка SQL MS ACCESS поддерживаются в MS SQL.
Часто встречается инструкция TRANSFORM, создающая 'перекрестный' набор данных. Аналога в MS SQL для этой инструкции нет.
Предлагается Хранимая процедура, решающая эту проблему.
CREATE PROCEDURE crosstab
@source varchar(1000), --то, что между FROM и WHERE
@fields varchar(100)=null, --поля для отбора... по ним происходит группировка. Если не заданы, то результатом будет одна строка - итог по всем записям
@func varchar(100), -- функция вида sum(some_field), count... и так далее ... НЕДОПУСТИМО использовать в качестве аргумента * (звездочку)
@pivot varchar(1000), -- правило для формирования полей
@filtr varchar(1000)=null, -- Условия отбора (то, что в запросе следует после WHERE)
@into varchar(100)=null -- Таблица, которая будет создана и заполнена результатами выполнения процедуры (опционально)
AS
DECLARE
@select VARCHAR(8000),
@sql VARCHAR(8000),
@sql1 VARCHAR(8000),
@sql2 VARCHAR(8000),
@sql3 VARCHAR(8000),
@sql4 VARCHAR(8000),
@sql5 VARCHAR(8000),
@sql6 VARCHAR(8000),
@sql7 VARCHAR(8000),
@sql8 VARCHAR(8000),
@sql9 VARCHAR(8000),
@sqlend VARCHAR(8000),
@delim VARCHAR(1),
@sqllen numeric(10),
@sqlnum numeric(10),
@pvalue VARCHAR(100),
@filtr1 varchar(1000)
SET NOCOUNT ON
SET ANSI_WARNINGS OFF
SET @filtr1=isnull(' AND '+@filtr, '')
EXEC ('SELECT DISTINCT ' + @pivot + ' as pivot INTO ##pivot FROM ' + @source + ' WHERE '
+ @pivot + ' Is Not Null '+ @filtr1)
SELECT @sql='',
@sql1='',
@sql2='',
@sql3='',
@sql4='',
@sql5='',
@sql6='',
@sql7='',
@sql8='',
@sql9='',
@select='SELECT '+isnull(@fields+',',''),
@func=stuff(@func, len(@func), 1, ' END)')
SELECT top 1 @delim = CASE ISNUMERIC(pivot) WHEN 1 THEN '' ELSE '''' END FROM ##pivot
DECLARE cur_pivot_cursor scroll CURSOR FOR select pivot from ##pivot order by pivot
OPEN cur_pivot_cursor
FETCH NEXT FROM cur_pivot_cursor
INTO @pvalue
SET @sqlnum = 1
WHILE @@FETCH_STATUS = 0
BEGIN
-- итерация складывания полей
SET @sql=@sql + '''' + convert(VARCHAR(100), @pvalue) + ''' = ' +
stuff(@func,charindex( '(', @func )+1, 0, 'CASE ' + @pivot +
' WHEN ' + @delim + convert(VARCHAR(100), @pvalue) + @delim + ' THEN ' ) + ', '
-- измеряем длину строки
SET @sqllen = len(@sql)
-- проверяем длину строки, если размер превышен,
-- 1-й проход
IF @sqllen > 7800 and @sqlnum = 1
BEGIN
SET @sql1 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 2-й проход
IF @sqllen > 7800 and @sqlnum = 2
BEGIN
SET @sql2 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 3-й проход
IF @sqllen > 7800 and @sqlnum = 3
BEGIN
SET @sql3 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 4-й проход
IF @sqllen > 7800 and @sqlnum = 4
BEGIN
SET @sql4 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 5-й проход
IF @sqllen > 7800 and @sqlnum = 5
BEGIN
SET @sql5 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 6-й проход
IF @sqllen > 7800 and @sqlnum = 6
BEGIN
SET @sql6 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 7-й проход
IF @sqllen > 7800 and @sqlnum = 7
BEGIN
SET @sql7 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 8-й проход
IF @sqllen > 7800 and @sqlnum = 8
BEGIN
SET @sql8 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- 9-й проход
IF @sqllen > 7800 and @sqlnum = 9
BEGIN
SET @sql9 = @sql
SET @sqlnum = @sqlnum + 1
SET @sql = ''
SET @sqllen = 0
END
-- получаем следующую строку
FETCH NEXT FROM cur_pivot_cursor
INTO @pvalue
END
-- курсор больше не нужен
CLOSE cur_pivot_cursor
DEALLOCATE cur_pivot_cursor
DROP TABLE ##pivot
-- убираем лишнюю запятую в конце всей серии срок
IF len(@sql) > 0
SELECT @sql=left(@sql, len(@sql)-1)
ELSE
BEGIN
IF LEN(@sql9)>0
SELECT @sql9=left(@sql9, len(@sql9)-1)
ELSE IF LEN(@sql8)>0
SELECT @sql8=left(@sql8, len(@sql9)-1)
ELSE IF LEN(@sql7)>0
SELECT @sql7=left(@sql7, len(@sql7)-1)
ELSE IF LEN(@sql6)>0
SELECT @sql6=left(@sql6, len(@sql6)-1)
ELSE IF LEN(@sql5)>0
SELECT @sql5=left(@sql5, len(@sql5)-1)
ELSE IF LEN(@sql4)>0
SELECT @sql4=left(@sql4, len(@sql4)-1)
ELSE IF LEN(@sql3)>0
SELECT @sql3=left(@sql3, len(@sql3)-1)
ELSE IF LEN(@sql2)>0
SELECT @sql2=left(@sql2, len(@sql2)-1)
ELSE IF LEN(@sql1)>0
SELECT @sql1=left(@sql1, len(@sql1)-1)
END
SET @filtr1=isnull(' WHERE '+@filtr, '')
set @sqlend=isnull(' INTO '+@into,'')+' FROM '+@source+@filtr1+isnull(' GROUP BY '+@fields,'')
EXEC (@select+@sql1+@sql2+@sql3+@sql4+@sql5+@sql6+@sql7+@sql8+@sql9+@sql+@sqlend)
SET ANSI_WARNINGS ON
GO
|
Эта процедура создает строку запроса и выполняет ее командой EXEC. Максимальная длина этого запроса около 85 килобайт.
Выполнить процедуру можно примерно так:
EXEC crosstab
'your_table_name1 a INNER JOIN your_table_name2 b ON a.id=b.id',
'a.field1, b.field2',
'count(a.id)',
'right(left(convert(varchar,b.datetime_field,121),13),2)',
'b.datetime_field between ''20040701'' and getutcdate() and a.id > 1000',
'##temp'
|
Описание входных параметров есть выше.
|
|
|
| Отзывы и оценки |
 |
| MaratSH | 26 февраля 2009, 06:34 | Оценка: N/A |
вот посмотрите, забацал вариант sql.ru/forum/actualthread.aspx?tid=642434 |
 |
| MaratSH | 25 февраля 2009, 22:49 | Оценка: N/A |
ведь компилятор (наверное в режиме по умолчанию? другого не встречал) на MS SQL 2005 не даст такое откомпилировать! Поле pivot формируется динамически, а используется статически. В общем, наверное это таоке дело допускается на каких то версиях MS SQL? А нельзя ли было написать без этой пурги? |
 |
| MaratSH | 25 февраля 2009, 22:46 | Оценка: 1 |
здесь хоть кто-нибудь проверял этот запрос? Особенно те, кто похвалил, что она работает :))))) ВО НАСМЕШИЛИ!!! И что, тут на sql.ru все такое? УЖАС!!! |
 |
| MaratSH | 25 февраля 2009, 22:44 | Оценка: 1 |
Что за пурга? Вы посмотрите хотя бы на эти две строки:
SELECT top 1 @delim = CASE ISNUMERIC(pivot) WHEN 1 THEN '' ELSE '''' END FROM ##pivot
DECLARE cur_pivot_cursor scroll CURSOR FOR select pivot from ##pivot order by pivot
Что у вас за такое поле pivot ???? Вы что??? Изначально процедура писалась так, чтобы это поле было неизвестным для процедуры, а тут вдруг конкретное поле с именем pivot!
ЛАЖА! |
 |
| sdfgsdfgsdf | 04 июня 2008, 05:10 | Оценка: 1 |
этописцец... такой тормосяки еще невидел... автору - учить SQL !!! - уже давно есть и покультурней и пошустрей... |
 |
| RADDD | 22 мая 2008, 19:23 | Оценка: 5 |
>Ета пурга не работает ((( Работает на раз!!! Сенкс , - день работы точно съэкономил! |
 |
| Shovgenyuk | 15 апреля 2008, 12:08 | Оценка: N/A |
EXEC crosstab 'va', 'IDAnalizDoc', 'count(IDAnalizDoc)', '', '', 'tva'
Результат: Server: Msg 208, Level 16, State 1, Procedure crosstab, Line 54 Invalid object name '##pivot'. Server: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'as'.
---------
Ета пурга не работает ((( |
 |
| MsDatabaseru | 19 октября 2007, 14:43 | Оценка: N/A |
версия cross-tab от ицик бен гана. вещь! http://www.softmatics.ru/sql/10.htm |
 |
| Гоша Мутный | 11 октября 2007, 11:50 | Оценка: N/A |
| Идите нафиг... Четал.... ниасилил...... |
 |
| AlexB | 22 июля 2007, 19:29 | Оценка: 1 |
| ##pivot и ##temp - это типа глобальные временные таблицы??? Автор - опомнись! |
 |
| Dln_new | 05 июля 2007, 10:28 | Оценка: 4 |
| Это конечно хорошо посмотрите Ицик Бен Ган как это сделал! |
 |
| 14 марта 2007, 01:58 | Оценка: 5 |
| отличная процедура |
 |
| Евгений | 08 сентября 2006, 07:42 | Оценка: 5 |
| главное что оно работает! |
 |
| S. Fedorenko | 15 июня 2006, 14:08 | Оценка: 5 |
| Очень помогли. Спасибо. |
 |
| BusyMan | 30 мая 2005, 11:30 | Оценка: 5 |
| Фантазия не-человеческой мысли... Среди нас инопланетяне! |
 |
| Раиль | 20 мая 2005, 07:21 | Оценка: 5 |
| Очень полезная процедура |
 |
|
Необходимо войти на сайт, чтобы оставлять комментарии | |
|
|
|
|
| Раздел FAQ: Microsoft SQL Server / Скрипты / T-SQL / Хранимая процедура crosstab - аналог TRANSFORM MS ACCESS |
|
|