1

I have this procedure :

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

Which can be run like this:

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

I'm using this kind of query:

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

Ifdbo.##HTMLtoMARKDOWNwas declared as a function (CREATE FUNCTION dbo.HTMLtoMARKDOWN @text nvarchar(500))), I could have written this:

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

I'm not allowed to use functions, so how I can do that kind of thing with a temporary procedure?

user2284570
  • 2,891
  • 3
  • 26
  • 74
  • 1
    Have a look at cross apply. – Keith John Hutchison Jul 31 '14 at 21:31
  • @kjtl : Ok. Can you detail an example in an answer, I can't see how I can use that kind of `JOIN`in my case. – user2284570 Jul 31 '14 at 21:40
  • If you could add the function definition to the question then yes. – Keith John Hutchison Aug 01 '14 at 12:25
  • @kjtl : This question is an extension of [this answer](http://stackoverflow.com/a/25049499/2284570). – user2284570 Aug 02 '14 at 11:22
  • I've got it working here in SQL Server 2012. Turned out there was no need for cross apply. What is the reason you are not allowed to use functions? – Keith John Hutchison Aug 03 '14 at 03:04
  • @kjtl : This [reason](http://meta.stackexchange.com/a/235082/242800). I don't think `ALTER`statements will work, since I don't have write access. Take a look at the [query](http://data.stackexchange.com/stackoverflow/query/edit/193616). – user2284570 Aug 03 '14 at 18:19
  • Just make everything temporary then. If you can create a temporary function or procedure you would have alter access the temporary objects you create. Alternatively, extract the raw data, load into your own sql express database and take it from there. – Keith John Hutchison Aug 03 '14 at 21:13
  • @kjtl : The main idea of the data explorer site is to share queries. Moreover I don't want to install windows. So I'd like to avoid the download part. Is there a shorter way than using `ALTER`kind of statements. – user2284570 Aug 03 '14 at 21:26
  • Just use create instead of alter. If you can create temporary stored procedures then you should also be able to create temporary functions. If you make me aware of how to test in the environment then I'll create an answer to work in that environment. I'll be back online tomorrow. – Keith John Hutchison Aug 03 '14 at 23:58
  • @kjtl : to create a query based on mine, just click on[`fork query`](http://data.stackexchange.com/stackoverflow/query/fork/193616) [here](http://data.stackexchange.com/stackoverflow/query/193616/highest-flag-close-votes-per-questions-with-more-control-than-in-the-flag-queue?Limit=38369&opt.withExecutionPlan=true). – user2284570 Aug 04 '14 at 07:58
  • I modified the query to change the results using the stored procedure and ran into timeout issues. You're back to taking the results offline and processing those results. You could use python. No need for a windows machine. – Keith John Hutchison Aug 04 '14 at 23:20
  • I changed the procedure to handle the timeout issues. Posted the answer. It works. – Keith John Hutchison Aug 05 '14 at 01:29
  • @kjtl : SQL server 2014? I doubt it can work without windows. – user2284570 Aug 05 '14 at 08:34
  • If you can use the procedure, while not run a cursor and call the procedure on each row? – Tim3880 May 25 '15 at 23:38
  • @Tim3880 : I don’t know what is a cursor in SQL. I saw [this](http://stackoverflow.com/a/6167687/2284570) but the answer doesn’t allow to return statements from procedures. – user2284570 May 25 '15 at 23:44
  • Are you using SQL server? It's a very straight forward concept. You can seach it and learn it quickly. If you need help, I can post a small one for you. – Tim3880 May 25 '15 at 23:47

2 Answers2

0

This one works by applying the stored procedure to the distinct reasons rather than processing the whole set.

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
Keith John Hutchison
  • 4,955
  • 11
  • 46
  • 64
0

To update rows using a stored procedure, you need cursor:

    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;

If you intent to return a record set (like select), you need a temp table or in-memory table:

    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;

The SET NOCOUNT ON/OFF are required if you want to return the row to a caller such as C#, PHP, where you also want to make above lines into one stored procedure.

Tim3880
  • 2,563
  • 1
  • 11
  • 14
  • Nice I idea, but I don’t have write permissions. – user2284570 May 26 '15 at 00:07
  • The second one does not update the table. If you can't create in memory table, you need do it in your own code. In your own code (php,c, ...), you can query the original table, fetch each record, pass it to the stored procedure, get the processed result. – Tim3880 May 26 '15 at 00:15
  • You missed [this problem](http://stackoverflow.com/a/22372569/2284570) which make I can’t use`@TMP`in [ReasonsPerPost](http://data.stackexchange.com/stackoverflow/query/193616/). – user2284570 May 26 '15 at 17:50
  • Yes you can use @TMP. Try http://data.stackexchange.com/stackoverflow/query/edit/319148. As long as the variable is in scope (before GO), you can use it. – Tim3880 May 26 '15 at 19:46