【问题标题】:Call Procedure for each Row without using a cursor and set the row with the result of the procedure在不使用游标的情况下为每一行调用过程,并使用过程的结果设置行
【发布时间】:2014-09-24 00:03:01
【问题描述】:

我有这个程序:

CREATE PROC dbo.##HTMLtoMARKDOWN @text nvarchar(500),
                                 @returnText nvarchar(500) output

AS
BEGIN
    DECLARE @counter tinyint
    SET @counter=1

    WHILE CHARINDEX('**', @text, 1) > 0
    BEGIN
        SELECT @text = STUFF(@text, 
                    CHARINDEX('**', @text, 1), 
                    2, 
                    IIF(@counter%2=0,'<br><b>','</b>')),
                @counter = @counter + 1
    END
SET @returnText = @text
END
GO

可以这样运行:

DECLARE @returnText nvarchar(500)
EXEC dbo.##HTMLtoMARKDOWN '**a** **b** **c**', @returnText output

我正在使用这种查询:

Select, IIF(IsUniversal=0,'TRUE','FALSE') as [Is Universal?],
    MarkdownMini as [Off Topic Reason]
From CloseAsOffTopicReasonTypes
group by IsUniversal, MarkdownMini

如果dbo.##HTMLtoMARKDOWN被声明为函数(CREATE FUNCTION dbo.HTMLtoMARKDOWN @text nvarchar(500))),我可以这样写:

Select, IIF(IsUniversal=0,'TRUE','FALSE') as [Is Universal?],
        dbo.HTMLtoMARKDOWN(MarkdownMini) as [Off Topic Reason]
From CloseAsOffTopicReasonTypes
group by IsUniversal, MarkdownMini

我不允许使用函数,那我怎么能用一个临时过程来做那种事情呢?

【问题讨论】:

  • 看看交叉申请。
  • @kjtl:好的。您能否在答案中详细说明一个示例,我看不出如何在我的情况下使用那种JOIN
  • 如果您可以将函数定义添加到问题中,那么可以。
  • @kjtl : 这个问题是this answer的扩展。
  • 我已经让它在 SQL Server 2012 中工作了。原来不需要交叉应用。不允许使用函数的原因是什么?

标签: sql sql-server sql-server-2014 dataexplorer


【解决方案1】:

这一方法通过将存储过程应用于不同的原因而不是处理整个集合来工作。

CREATE PROC dbo.##HTMLtoMARKDOWN @text nvarchar(500),
                                 @returnText nvarchar(500) output

AS
BEGIN
    DECLARE @counter tinyint
    SET @counter=1

    WHILE CHARINDEX('**', @text, 1) > 0
    BEGIN
        SELECT @text = STUFF(@text, 
                    CHARINDEX('**', @text, 1), 
                    2, 
                    IIF(@counter%2=0,'<br><b>','</b>')),
                @counter = @counter + 1
    END
    SET @counter=1
    WHILE CHARINDEX('*', @text, 1) > 0
    BEGIN
        SELECT @text = STUFF(@text, 
                    CHARINDEX('*', @text, 1), 
                    1, 
                    IIF(@counter%2=0,'<br><i>','</i>')),
                @counter = @counter + 1
    END
    -- SET @returnText = @text
    SET @returnText = @text
END
GO

DECLARE @returnText nvarchar(500)

--taken from http://meta.stackexchange.com/a/237237/242800
;with ReasonsPerPost as
(
   -- get the count of each close/flag reason per post
   select TOP 100 PERCENT -- Only use with clustered index.
      Posts.Id,
      PendingFlags.CloseReasonTypeId,
      IIF(CloseReasonTypeId<>102,CloseReasonTypes.Name,MarkdownMini) as Name,
      count(PendingFlags.CloseReasonTypeId) as TotalByCloseReason
   from Posts
      INNER JOIN PendingFlags on PendingFlags.PostId = Posts.Id
      INNER JOIN CloseReasonTypes on CloseReasonTypes.Id=PendingFlags.CloseReasonTypeId
      LEFT OUTER JOIN CloseAsOffTopicReasonTypes on CloseAsOffTopicReasonTypes.id=PendingFlags.CloseAsOffTopicReasonTypeId
   where Posts.ClosedDate IS NULL -- The question is not closed.
          and PendingFlags.FlagTypeId in (14,13) -- Exclude reopen votes
   group by Posts.id, CloseReasonTypes.Name, MarkdownMini, PendingFlags.CloseReasonTypeId
   order by TotalByCloseReason desc
),
TopPerPost as
(
  -- create a row number to order the results by the close reason totals
   select Id,
      CloseReasonTypeId,
      Name,
      ReasonsPerPost.TotalByCloseReason,
      row_number() over(partition by Id order by TotalByCloseReason desc) seq
   from ReasonsPerPost
   where Name is NOT NULL
)



select TOP ##Limit:int?38369## -- This number may grow, or get removed the day the server will have enough RAM.
   Posts.Id as [Post Link], -- Question title.
   Count(PendingFlags.PostId) as [Number of pending flags], -- Number of pending flags per questions.
   TopPerPost.Name as [The most common vote reason],
   Posts.OwnerUserId as [User Link], -- Let click on the colum to see if the same user ask off-topic questions often.
   Reputation as [User Reputation], -- Interesting to see that such questions are sometimes asked by high rep users.
   Posts.Score as [Votes], -- Interesting to see that some questions have more than 100 upvotes.
   Posts.AnswerCount as [Number of Answers], -- I thought we shouldn't answer on off-topic post.
   Posts.ViewCount,
   Posts.FavoriteCount as [Number of Stars], -- Some questions seems to be very helpfull :) .
   Posts.CreationDate as [Asked on], -- The older is the question, the more is the chance that flags on them can't get reviewed.
   Posts.LastActivityDate as [last activity], -- Similar effect as with Posts.CreationDate.
   Posts.LastEditDate as [modified on]
into #results 
from Posts
   INNER JOIN PendingFlags on PendingFlags.PostId = Posts.Id
   LEFT OUTER JOIN Users on Users.id = posts.OwnerUserId
   LEFT OUTER JOIN TopPerPost on Posts.id=TopPerPost.id
where seq=1
group by Posts.id, Posts.OwnerUserId, TopPerPost.Name, Reputation, Posts.Score, Posts.FavoriteCount, Posts.AnswerCount, Posts.CreationDate, Posts.LastActivityDate, Posts.LastEditDate, Posts.ViewCount
order by [Number of pending flags] desc, [The most common vote reason], Score desc, Reputation desc, FavoriteCount desc, ViewCount desc, Posts.CreationDate asc, LastActivityDate, LastEditDate -- Questions with more flags have more chance to get them handled, and the higher is the probabilty that the question is off-topic (since several users already reviewed the question).

select distinct [The most common vote reason] into #reasons from #results
ALTER TABLE #reasons 
ADD id INT IDENTITY(1,1), html nvarchar(500)

create nonclustered index results_reasons_index
on #results ([The most common vote reason]);

create unique nonclustered index reasons_index
on #reasons ([The most common vote reason]);

declare @id int
declare @maxId as int
declare @markdown as nvarchar(500)
declare @html as nvarchar(500)
select @maxId = max(id) from #reasons 
set @id = 0

while ( @id < @maxId )
begin
    set @id = @id + 1
    select @markdown = [The most common vote reason] from #reasons where id = @id
    exec dbo.##HTMLtoMARKDOWN @text = @markdown, @returnText = @html output
    update #reasons set html = @html where id = @id
end

update #results set [The most common vote reason] = #reasons.html
from #results 
inner join #reasons 
on #results.[The most common vote reason] 
=  #reasons.[The most common vote reason] 

select * from #results

【讨论】:

  • 把程序用在CloseAsOffTopicReasonTypes.MarkdownMini开头不是更好吗?
  • 那行得通。也会让它更快。您要进行编辑吗?
  • 即使创建一个临时表我也找不到方法。
  • 如果您发现它回答了您的问题,请接受答案。
【解决方案2】:

要使用存储过程更新行,您需要游标:

    DEClARE @akey int, @text NVARCHAR(500),@retText NVARCHAR(500);
    DECLARE  c CURSOR LOCAL FAST_FORWARD FOR SELECT aid, MarkdownMini 
    FROM CloseAsOffTopicReasonTypes;
    OPEN c;
    FETCH NEXT FROM c into @akey, @text;
    WHILE @@FETCH_STATUS=0 BEGIN
        EXEC dbo.##HTMLtoMARKDOWN @TEXT, @retText output;
        UPDATE CloseAsOffTopicReasonTypes 
           SET MarkDown = @retText WHERE aid = @akey;
        FETCH NEXT FROM c into @akey, @text;
    END;
    DEALLOCATE c;

如果您打算返回记录集(如 select),则需要临时表或内存表:

    DECLARE @TMP TABLE (akey int, MarkDown nvarchar(800) );
    SET NOCOUNT ON;
    DEClARE @akey int, @text NVARCHAR(500),@retText NVARCHAR(500);
    DECLARE  c CURSOR LOCAL FAST_FORWARD FOR SELECT aid, MarkdownMini 
           FROM CloseAsOffTopicReasonTypes;
    OPEN c;
    FETCH NEXT FROM c into @akey, @text;
    WHILE @@FETCH_STATUS=0 BEGIN
        EXEC dbo.##HTMLtoMARKDOWN @TEXT, @retText output;
        --UPDATE CloseAsOffTopicReasonTypes SET MarkDown = @retText WHERE aid = @akey;
        INSERT INTO @TMP (akey, MarkDown) values(@akey, @retText);
        FETCH NEXT FROM c into @akey, @text;
    END;
    DEALLOCATE c;
    SET NOCOUNT OFF;
    SELECT * FROM @TMP;

如果您想将该行返回给调用者,例如 C#、PHP,则需要 SET NOCOUNT ON/OFF,您还想在其中将上述行变成一个存储过程。

【讨论】:

  • 好主意,但我没有写权限。
  • 第二个不更新表。如果您无法在内存表中创建,则需要在自己的代码中进行。在自己的代码(php,c, ...)中,可以查询原表,取出每条记录,传给存储过程,得到处理后的结果。
  • 你错过了this problem,这让我无法使用@TMPin ReasonsPerPost
  • 是的,您可以使用@TMP。试试data.stackexchange.com/stackoverflow/query/edit/319148。只要变量在范围内(在 GO 之前),就可以使用它。
猜你喜欢
  • 1970-01-01
  • 2010-12-12
  • 1970-01-01
  • 2017-07-17
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多