Как мне НЕ использовать t-sql курсор, когда печать строк необходима? - PullRequest
4 голосов
/ 08 сентября 2010

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

Я начал с того, что по сути является комбинацией того, что вы видите ниже. Я бы выбрал один столбец для каждой строки, где этот столбец был целым wikimarkup строки. Это отлично работало, но я хотел напечатать текст до и после результатов. Я взломал это с помощью нескольких союзов. Я хотел бы объединить заголовок с набором результатов, а затем объединить все это с нижним колонтитулом. Но ТО, я должен был вставить строку текста между каждой строкой, и ЭТО было той частью, которую я не мог понять без использования курсора. Короче говоря:

Как выбрать группу записей с жестко закодированной строкой перед каждой строкой результатов?

В моем случае каждой строке должна предшествовать строка |-.

set ansi_nulls on
go
set quoted_identifier on
go

alter procedure DocTable
    @TableName varchar(256)
as
begin
    set nocount on;

    declare @WikiDocData table
    (
        Name nvarchar(256),
        [Type] nvarchar(256),
        Nullable nvarchar(256),
        [Default] nvarchar(256),
        [Identity] nvarchar(256),
        [Description] nvarchar(max)
    )

    insert into @WikiDocData
        select
            c.name as Name,
            tp.name + 
                ' (' + 
                (case when c.max_length = -1 then 'MAX' else convert(nvarchar(256),c.max_length) end) +
                ', ' +
                convert(nvarchar(256), c.scale) +
                ', ' +
                convert(nvarchar(256), c.[precision]) + ')'
                as [Type (L,S,P)],
            (case when c.is_nullable = 1 then 'Yes' else '' end) as Nullable,
            isnull(d.[definition], '') as [Default],
            (case when c.is_identity = 1 then 'Yes' else '' end) as [Identity],
            convert(nvarchar(max),isnull(p.value, '')) as [Description]
        from
            sys.tables t 
            inner join sys.columns c on t.object_id = c.object_id
            left join sys.extended_properties p on c.object_id = p.major_id and c.column_id = p.minor_id
            inner join sys.types tp on c.system_type_id = tp.system_type_id
            left join sys.default_constraints d on c.default_object_id = d.object_id and c.column_id = d.parent_column_id
        where
            t.[name] = @TableName 
            and tp.name <> 'sysname'
        order by
            t.object_id,
            c.column_id

    /* Dear reader, if you know how to do this without a cursor, please let me know! */

    -- Output header
    print '{| cellpadding="4" cellspacing="0" border="1"'
    print '! Name !! Type (L,S,P) !! Nullable !! Default !! Identity !! Description'

    -- Output each row and row separator
    declare @WikiRow nvarchar(max)
    declare @GetWikiRow cursor

    set @GetWikiRow = cursor for
        select
            '| ' +
            Name + ' || ' +
            [Type] + ' || ' +
            Nullable + ' || ' +
            [Default] + ' || ' +
            [Identity] + ' || ' +
            [Description]
        from
            @WikiDocData

    open @GetWikiRow fetch next from @GetWikiRow into @WikiRow while @@fetch_status = 0
    begin
        print '|-'
        print @WikiRow
        fetch next from @GetWikiRow into @WikiRow
    end
    close @GetWikiRow
    deallocate @GetWikiRow

    -- Output footer
    print '|}'

end
go

Это в настоящее время работает. При запуске aspnet_Membership он выводит только одно и то же:

{| cellpadding="4" cellspacing="0" border="1"
! Name !! Type (L,S,P) !! Nullable !! Default !! Identity !! Description
|-
| ApplicationId || uniqueidentifier (16, 0, 0) ||  ||  ||  || 
|-
| UserId || uniqueidentifier (16, 0, 0) ||  ||  ||  || 
|-
| Password || nvarchar (256, 0, 0) ||  ||  ||  || 
|-
| PasswordFormat || int (4, 0, 10) ||  || ((0)) ||  || 
|-
| PasswordSalt || nvarchar (256, 0, 0) ||  ||  ||  || 
|-
| MobilePIN || nvarchar (32, 0, 0) || Yes ||  ||  || 
|-
| Email || nvarchar (512, 0, 0) || Yes ||  ||  || 
|-
| LoweredEmail || nvarchar (512, 0, 0) || Yes ||  ||  || 
|-
| PasswordQuestion || nvarchar (512, 0, 0) || Yes ||  ||  || 
|-
| PasswordAnswer || nvarchar (256, 0, 0) || Yes ||  ||  || 
|-
| IsApproved || bit (1, 0, 1) ||  ||  ||  || 
|-
| IsLockedOut || bit (1, 0, 1) ||  ||  ||  || 
|-
| CreateDate || datetime (8, 3, 23) ||  ||  ||  || 
|-
| LastLoginDate || datetime (8, 3, 23) ||  ||  ||  || 
|-
| LastPasswordChangedDate || datetime (8, 3, 23) ||  ||  ||  || 
|-
| LastLockoutDate || datetime (8, 3, 23) ||  ||  ||  || 
|-
| FailedPasswordAttemptCount || int (4, 0, 10) ||  ||  ||  || 
|-
| FailedPasswordAttemptWindowStart || datetime (8, 3, 23) ||  ||  ||  || 
|-
| FailedPasswordAnswerAttemptCount || int (4, 0, 10) ||  ||  ||  || 
|-
| FailedPasswordAnswerAttemptWindowStart || datetime (8, 3, 23) ||  ||  ||  || 
|-
| Comment || ntext (3000, 0, 0) || Yes ||  ||  || 
|}

Новый код с ответом LittleBobbyTables (он короче, но требует много конкатенации строк и не может печатать, когда в разметке более 8000 символов):

set ansi_nulls on
go
set quoted_identifier on
go

alter procedure DocTable
    @TableName varchar(256)
as
begin
    set nocount on;

    -- Output header 
    print '{| cellpadding="4" cellspacing="0" border="1"' 

    -- Output each row and row separator 
    declare @WikiRow nvarchar(max) 
    set @WikiRow = '! Name !! Type (L,S,P) !! Nullable !! Default !! Identity !! Description'

    select
        @WikiRow = @WikiRow + 
        char(10) + '|- ' + char(10) + '| ' +
        c.name + ' || ' + 
        tp.name + 
            ' (' + 
            (case when c.max_length = -1 then 'MAX' else convert(nvarchar(256),c.max_length) end) +
            ', ' +
            convert(nvarchar(256), c.scale) +
            ', ' +
            convert(nvarchar(256), c.[precision]) + ')' + ' || ' + 
        (case when c.is_nullable = 1 then 'Yes' else '' end) + ' || ' + 
        isnull(d.[definition], '') + ' || ' + 
        (case when c.is_identity = 1 then 'Yes' else '' end) + ' || ' + 
        convert(nvarchar(max),isnull(p.value, ''))
    from
        sys.tables t 
        inner join sys.columns c on t.object_id = c.object_id
        left join sys.extended_properties p on c.object_id = p.major_id and c.column_id = p.minor_id
        inner join sys.types tp on c.system_type_id = tp.system_type_id
        left join sys.default_constraints d on c.default_object_id = d.object_id and c.column_id = d.parent_column_id
    where
        t.[name] = @TableName 
        and tp.name <> 'sysname'
    order by
        t.object_id,
        c.column_id

    print @WikiRow     

    -- Output footer 
    print '|}' 

end
go

Ответы [ 4 ]

4 голосов
/ 08 сентября 2010

Вот процедура для печати длинных переменных varchar (max) (для работы PRINT не требуется расстояние между CRLF, превышающим максимальное пороговое значение, поскольку оно в основном берет строку и перемещает ее в буфере в виде «строк», а затем печатает буфер, когда он получает более 4000 символов):

CREATE PROCEDURE [usp_PrintLongSQL]
    @sql varchar(max)
AS
BEGIN
    DECLARE @CRLF AS varchar(2)
    SET @CRLF = CHAR(13) + CHAR(10)

    DECLARE @input AS varchar(max)
    SET @input = @sql

    DECLARE @output AS varchar(max)
    SET @output = ''

    WHILE (@input <> '')
    BEGIN
        DECLARE @line AS varchar(max)
        IF CHARINDEX(@CRLF, @input) > 0
            SET @line = LEFT(@input, CHARINDEX(@CRLF, @input) - 1) + @CRLF
        ELSE
            SET @line = @input

        IF LEN(@input) - LEN(@line) > 0
            SET @input = RIGHT(@input, LEN(@input) - LEN(@line))
        ELSE
            SET @input = ''

        SET @output = @output + @line
        IF LEN(@output) > 4000
        BEGIN
            PRINT @output
            SET @output = ''
        END
    END

    IF @output <> ''
        PRINT @output
END

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

2 голосов
/ 08 сентября 2010

Обновление: Per Cade Roux и Chris, это не работает при печати более 8000 символов.Я оставляю это как предупреждение.

Вы можете использовать переменную для многократного добавления строк.Попробуйте это:

-- Output header 
print '{| cellpadding="4" cellspacing="0" border="1"' 
print '! Name !! Type (L,S,P) !! Nullable !! Default !! Identity !! Description' 

-- Output each row and row separator 
declare @WikiRow nvarchar(max) 
set @WikiRow = ''

select @WikiRow = @WikiRow + 
        '|- ' + char(10) + '| ' +
        Name + ' || ' + 
        [Type] + ' || ' + 
        Nullable + ' || ' + 
        [Default] + ' || ' + 
        [Identity] + ' || ' + 
        [Description] + char(10) 
    from 
        @WikiDocData 

print left(@WikiRow, len(@WikiRow) - 1)

-- Output footer 
print '|}' 
1 голос
/ 09 ноября 2015

Это можно сделать с помощью предложения Offset Fetch в SQL Server 2012 или более поздней версии.

Использование таблицы AdventureWorks Production.Products ...

DECLARE @Output varchar(8000) = '';

-- 'Print' function only prints 8000 non-unicode chars max.  Let's print 10 at a time.  Use Fetch Next with Offset.  (Sql Svr 2012+)
DECLARE @rowNum int = 0;
DECLARE @numRows int;
SELECT @numRows = count(ProductID) from Production.Products;

WHILE @rowNum < @numRows
BEGIN

    SELECT @Output = @Output + '
    IF (@someVariable = ''' + ProductNumber + ''')      BEGIN;      RETURN ''' + ProductName + ''';     END;'
        FROM    Production.Products
        ORDER BY ProductID
        OFFSET @rowNum ROWS FETCH NEXT 10 ROWS ONLY;        -- 10 rows at a time so can print without fear of truncation.

    PRINT @Output;
    SET @Output = '';               -- reset for next set of rows
    SET @rowNum = @rowNum + 10;
END
0 голосов
/ 08 сентября 2010

Как выбрать группу записей с жестко закодированной строкой перед каждой строкой?

select '|-I am a hardcoded string with a newline following' 
        + char(10) + a.foo as foo
from bar a;

То есть просто вставьте жестко закодированную строку в столбец, который вы уже выбрали.Разделите их символом новой строки (char (10)) или, для DOS / Windows, переводом строки с возвратом каретки (char (13) + char (10)).

Редактировать: спасибо всем, кто указалчто оператор catenation равен "+", а не "||"в T-SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...