2010-09-07 50 views
4

一位同事和我編寫了這個存儲過程,該過程爲一個ScrewTurn wiki系統在wiki標記中記錄了一個數據庫表。最初,我沒有光標就寫了它,因爲直到今天,我甚至都不知道如何使用它!如何在不需要打印行時使用t-sql遊標?

我從下面看到的基本組合開始。我會爲每行選擇一列,其中該列是該行的整個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 
+0

不要忘了加上'類= 「wikitable排序」'表中定義:) – 2010-09-07 21:07:35

回答

2

更新: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 '|}' 
+0

哎呀,在那裏有太多+的跡象 – LittleBobbyTables 2010-09-07 21:17:48

+0

仍然有語法錯誤。我需要將第二個「設置」更改爲「選擇」。然後,我有問題在底部額外的回車。我也解決了這個問題。現在我有一個問題:如果這個變量超過8000個字符,會不會有問題?它可以仍然工作時,nvachar(最大)踢到一個單獨的頁面或任何? – Chris 2010-09-07 21:22:14

+1

@Chris varchar(max)會很好,但是在嘗試打印大型varchar(max)時會遇到問題 - PRINT無法處理它。我有一個自定義的SP來打印很長的東西,將它分解成幾個PRINT語句。 – 2010-09-07 21:28:29

-1

如何選擇每行前硬編碼字符串一堆記錄?

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

也就是說,僅僅是CONCAT硬編碼字符串到你已經選擇列。用一個新的行字符(char(10))分隔它們,或者對於DOS/Windows,使用回車換行符(char(13)+ char(10))將它們分開。

編輯:謝謝大家誰指出catenation運算符是「+」,而不是「||」在T-SQL中。

+0

''||將無法正常工作在'T-SQL'。它是'+' – Quassnoi 2010-09-07 21:00:46

+1

沒有|| T-SQL中的連接運算符。改用+。 – 2010-09-07 21:01:25

+0

||是甲骨文,男人;它是......讓我自己去理解它......一致,而不僅僅是連鎖。 – 2010-09-07 21:04:16

4

這裏有一個例程來打印LONG VARCHAR(最大值)的變量(它需要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 

我個人更喜歡使用這個,因爲這讓很多的其他代碼更簡單,更靈活的無光標(例如可以進入視圖或內聯表值函數的代碼更具可重用性)。

+0

這看起來很有用,我可以將它添加到我的有用腳本庫中。謝謝。你認爲這與LittleBobbyTables方法相結合總體上比使用遊標更好嗎? – Chris 2010-09-07 22:21:47

+0

@Chris是的,因爲PRINT不是我用於生產的東西,主要用於代碼生成。通常,我的生產代碼位於視圖中或(如果需要參數)內聯表值函數或(如果需要多個語句)存儲過程。代碼層次越低,重用越容易(以及更好的執行)。你的遊標代碼不能真正轉換成可重用對象,因爲它依賴於PRINT輸出(副作用 - 當你從PRINT打印的東西調用它時會發生什麼?)。 – 2010-09-08 17:49:42

+0

所有優點。這只是爲了快速和骯髒的文檔,我可能會認真對待它。 – Chris 2010-09-09 01:50:35

1

這可以使用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