2013-11-13 4 views
2

Я пытаюсь лучше понять логику на основе набора и упростить часть кода, над которым я работаю. Вот пример того, что я работаю с (он не работает в настоящее время по причинам, которые быстро становятся очевидными):Обработка операций на основе набора в SQL

SELECT 
    userid, 
    rn = ROW_NUMBER() OVER (ORDER BY username) 
    FROM user 
    WHERE username like 'test%' 

WHILE rn between 1 and 100 
    <RUN SP USING INFORMATION> 
WHILE rn between 101 and 200 
    <RUN SP WITH DIFFERENT INFORMATION> 

Для целей примера, предположим, что имеется 200 строк, которые удовлетворяют критериям в SELECT заявление. Также предположим, что мы не можем вносить изменения в таблицу user. Мой вопрос заключается в том, что без использования таблицы temp и без (надеюсь) с использованием цикла WHILE, как еще я могу справиться с этим?

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

+3

Это будет переписывание хранимых процедур, чтобы сделать это в соответствии с установленным образом ... вам нужно предоставить хотя бы один из них, если вам нужна помощь в решении, основанном на наборе – Twelfth

+0

К сожалению, я могу «Переписывайте эти SP (хотя ... может быть, я смогу вырвать из этого логику ...), поэтому мне было любопытно, будет ли еще один просто обработать эту часть и получить информацию от сообщества. Если нет, это вполне приемлемый ответ. –

+0

Я бы, скорее всего, быстро попал в кроличью яму, из которой я бы никогда не появился, если я начну вытаскивать логику из SP в мой «простой» сценарий. –

ответ

1

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

3

Несколько лет назад я написал утилиту на базе SQL специально для удовлетворения таких потребностей. Он подробно описан в этой статье на другом сайте здесь: http://www.sqlservercentral.com/scripts/Administration/69737/. (Если вы хотите, я могу включить его здесь, как хорошо, но это довольно долго)

Пожалуйста, обратите внимание на следующее по этому поводу:

  1. Она написана полностью в T-SQL, и
  2. It не использует никаких курсоров или циклов While.

Вы, вероятно, решить вашу проблему с помощью этого инструмента что-то вроде этого:

EXECUTE OVER_SET ' 
    IF {rn} between 1 and 100 
     <RUN SP USING INFORMATION> 
    IF {rn} between 101 and 200 
     <RUN SP WITH DIFFERENT INFORMATION> 
    ', 
@from = ' 
    (SELECT 
      userid, 
      rn = ROW_NUMBER() OVER (ORDER BY username) 
    FROM user 
    WHERE username like ''test%'') aa ' 
@subs1 = '{userid}=userid', 
@subs2 = '{rn}=rn', 
@quote = '"'  -- Allows you to use (") for quotes inside the 1st string 
; 

Поскольку это знак вверх сайт, я отправляю код ниже (длинный):

CREATE PROC 
    OVER_SET (
    @command AS NVARCHAR(MAX),  -- Template SQL command 
    @from AS NVARCHAR(MAX),  -- FROM..WHERE clause string 
    @subs1 AS NVARCHAR(MAX) = N'', -- Substitution parameters, these are 
    @subs2 AS NVARCHAR(MAX) = N'', -- of the form "<find>=<repl>" where: 
    @subs3 AS NVARCHAR(MAX) = N'', -- <find> will be searched for in @command, and 
        -- <repl> will replace it, if it was found 
        -- (typically, <repl> should be a column name 
        -- returned by the FROM clause) 
    @print AS BIT = 1,    -- 0 = suppress PRINT of the SQL before executing 
    @catch AS VARCHAR(12) = 'continue', 
        -- TRY/CATCH option parameters. Choices are: 
        -- 'continue' on an error, print a message & continue 
        -- 'ignore' attempt to suppress all errors 
        -- 'fail' try to re-raise the error 
        -- 'none' no TRY/CATCH blocks 
    @use_db AS NVARCHAR(255) = N'', -- DB to switch to befor execution of the SQL text 
    @quote AS NVARCHAR(8) = N'' -- search for this character & replace with ('). 
    ) 
AS 
-- 
DECLARE @qt AS NVARCHAR(1), @cr AS NVARCHAR(1); 
SELECT @qt = N'''',  @cr = N' 
'; 
DECLARE @find1 AS NVARCHAR(MAX), @prfx1 AS NVARCHAR(MAX), @sufx1 AS NVARCHAR(MAX) 
DECLARE @find2 AS NVARCHAR(MAX), @prfx2 AS NVARCHAR(MAX), @sufx2 AS NVARCHAR(MAX) 
DECLARE @find3 AS NVARCHAR(MAX), @prfx3 AS NVARCHAR(MAX), @sufx3 AS NVARCHAR(MAX) 
DECLARE @prtst AS NVARCHAR(MAX), @prfxC AS NVARCHAR(MAX), @sufxC AS NVARCHAR(MAX) 
DECLARE @newdb AS NVARCHAR(MAX), @declr AS NVARCHAR(MAX) 
DECLARE @NewCmd AS NVARCHAR(MAX), @GenCmd AS NVARCHAR(MAX) 
; 
SELECT 
@find1 = CASE WHEN @subs1 = N'' THEN N'' ELSE LEFT(@subs1,CHARINDEX(N'=',@subs1)-1) END, 
@prfx1 = CASE WHEN @subs1 = N'' THEN N'' ELSE N'REPLACE(' END, 
@sufx1 = CASE WHEN @subs1 = N'' THEN N'' ELSE N',@find1,'+RIGHT(@subs1,LEN(@subs1)-CHARINDEX(N'=',@subs1))+N')' END, 
@find2 = CASE WHEN @subs2 = N'' THEN N'' ELSE LEFT(@subs2,CHARINDEX(N'=',@subs2)-1) END, 
@prfx2 = CASE WHEN @subs2 = N'' THEN N'' ELSE N'REPLACE(' END, 
@sufx2 = CASE WHEN @subs2 = N'' THEN N'' ELSE N',@find2,'+RIGHT(@subs2,LEN(@subs2)-CHARINDEX(N'=',@subs2))+N')' END, 
@find3 = CASE WHEN @subs3 = N'' THEN N'' ELSE LEFT(@subs3,CHARINDEX(N'=',@subs3)-1) END, 
@prfx3 = CASE WHEN @subs3 = N'' THEN N'' ELSE N'REPLACE(' END, 
@sufx3 = CASE WHEN @subs3 = N'' THEN N'' ELSE N',@find3,'+RIGHT(@subs3,LEN(@subs3)-CHARINDEX(N'=',@subs3))+N')' END, 
@newdb = CASE WHEN @use_db= N'' THEN N'' ELSE N'USE [' + @use_db + N'];' + @cr END, 
@declr = N'DECLARE @_Num AS INT, @_Lin AS INT, @_Err AS NVARCHAR(MAX), @_Msg AS NVARCHAR(MAX);'[email protected] 
; 
;WITH 
[base] AS (SELECT cmd = @command), 
[quot] AS (SELECT cmd = CASE @quote WHEN N'' THEN cmd ELSE REPLACE(cmd, @quote, @qt) END FROM [base]), 
[dble] AS (SELECT cmd = N'N'[email protected]+REPLACE(cmd, @qt, @[email protected])[email protected] FROM [quot]), 
[prnt] AS (SELECT cmd = CASE @print WHEN 1 THEN N' PRINT '+cmd+';'[email protected] ELSE N'' END 
         + N' EXEC('+cmd+N');' FROM [dble]), 
[ctch] AS (SELECT cmd = 
    CASE @catch WHEN N'none' THEN cmd 
    ELSE N'BEGIN TRY'[email protected][email protected]+N'END TRY'[email protected]+N'BEGIN CATCH'[email protected] 
    + N' SELECT @_Num=ERROR_NUMBER(), @_Lin=ERROR_LINE(), @_Err=ERROR_MESSAGE()'[email protected] 
    + CASE @catch 
     WHEN N'continue' THEN 
       N' SELECT @_msg=''Continuing after Error(''+CAST(@_Num AS NVARCHAR)+'') at Line ''+CAST(@_Lin AS NVARCHAR)+''' 
         [email protected]+' ''[email protected]_Err;'[email protected] 
       +N' PRINT @_msg; '[email protected] 
       +N' PRINT '' ''; '[email protected] 
     WHEN N'ignore' THEN N' -- ignore = do nothing'[email protected] 
     WHEN N'fail' THEN 
       N' SELECT @_msg=''Failing after Error(''+CAST(@_Num AS NVARCHAR)+'') at Line ''+CAST(@_Lin AS NVARCHAR)+''' 
         [email protected]+' ''[email protected]_Err;'[email protected] 
       +N' RAISERROR(@_Num, 16, 1);'[email protected] 
       +N' PRINT '' ''; '[email protected] 
     ELSE N' --BAD else branch, shouldnt get here' END 
    + N'END CATCH;' END FROM [prnt]) 
SELECT 
    @NewCmd = @[email protected][email protected]+ N'@command' [email protected][email protected][email protected], 
    @command = cmd + @cr 
FROM [ctch] 
; 
SELECT @GenCmd = ' 
DECLARE @sql AS NVARCHAR(MAX); SET @sql = '''[email protected]+ [email protected]+ ''' 
;WITH 
    [[email protected]] AS (SELECT * FROM ' [email protected]+ ') 
, [[email protected]] AS (SELECT [-NewCmd] = ' [email protected]+ ' FROM [[email protected]]) 
, [[email protected]] AS (SELECT [-NewCmd] = [-NewCmd] FROM [[email protected]]) 
SELECT 
    @sql = @sql + '' 
'' + [-NewCmd] 
FROM [[email protected]] 
; 
EXEC sp_executesql @sql; 
' 
; 
EXEC sp_executesql @GenCmd 
, N'@command NVARCHAR(MAX), @from NVARCHAR(MAX), @find1 NVARCHAR(MAX), @find2 NVARCHAR(MAX), @find3 NVARCHAR(MAX)' 
, @command, @from, @find1, @find2, @find3 
; 

Вот пост-комментарии, которые включают в себя некоторые примеры:

Example Usages 

1) INSERT..EXECute: 
Demonstrates capturing the SELECT output from an EXECUTE OVER_SET that 
searches every database in the SQL Server Instance for routines with 
the work "cursor" in them. 
-- 

CREATE TABLE #temp (DB sysname, [Schema] sysname, Routine sysname); 
INSERT INTO #temp 
    EXECUTE OVER_SET ' 
    SELECT ROUTINE_CATALOG, ROUTINE_SCHEMA, ROUTINE_NAME 
     FROM [{db}].INFORMATION_SCHEMA.ROUTINES 
     WHERE ROUTINE_DEFINITION like "%cursor%"', 
    @from = 'sys.sysdatabases WHERE dbid > 4', 
    @subs1 = '{db}=name', 
    @quote = '"' 
; 
SELECT * from #temp; 
DROP table #temp; 

-- 
The @from argument returns the list of non-system databases in the server, 
and the @susbs1 argument "{db}=name" tells it to replace every instance 
of "{db}" in the command strings with the value of the [name] column (from 
sys.sysdatabases). Note also the @quote argument's value (") allows us to 
use a single quotation mark in the quoted command text instead of having 
to use double apostrophes (ie, ' "%cursor%" ', instead of ' ''%cursor%'' '). 

--====== 

2) Nested Example: 
Demonstrates, nesting OVER_SET execution to operate against the combination 
of to different sets, the second dependent on the first. Specifically, 
it searches every non-system database for every user that is a windows 
user or group, and then attempts to map them back to a server Login of 
the same name. 
-- 

EXECUTE OVER_SET ' 
    EXECUTE OVER_SET " 
     ALTER USER [{name}] WITH LOGIN = [{name}]; 
     PRINT `USER {name} has been mapped to its Login.`;", 
    @from = "sys.database_principals 
      WHERE (type_desc = ""WINDOWS_GROUP"" OR type_desc = ""WINDOWS_USER"") 
      AND name NOT like ""%dbo%"" AND name NOT LIKE ""%#%"" ", 
    @use_db = "{db}", 
    @subs1 = "{name}=name", 
    @catch = "continue", 
    @print = 1, 
    @quote = "`"; 
    ', 
    @from = 'sys.sysdatabases 
     WHERE dbid > 4', 
    @subs1 = '{db}=name', 
    @catch = 'continue', 
    @print = 0, 
    @quote = '"'; 

-- 
The outer OVER_SET uses the @from argument to return the list of all databases 
which the @subs1 argument "{db}=name", uses to modify the inner OVER_SET 
commands @use_db argument, cuasing the inner execution to USE [{db}} to each 
database in turn. The inner execution's @from argument returns the list 
of database users that are WINDOWS_* user or group, and the @subs1 ({name}=name) 
cause the "{name}" token to be replaced with the value of the [name] column 
from the database_principals table. 

Note that two different @quote characters are used (("), then (`)), removing 
the need for double or even quadruple apostrophes in the inner command text. 
(also note, that the @from argument text does not benefit from this, and can 
only use the outer command quote (") becuase it is part of the outer command 
text argument. 
0

Можно ли использовать UDFs instad SP? В этом случае вы можете включить UDF с параметрами из инструкции SELECT за один раз. Очевидно, UDF имеют ограничения, но я думаю, это зависит от сложности вашего SP.

Смежные вопросы