【问题标题】:Remove all comments from a text in T-SQL从 T-SQL 中的文本中删除所有注释
【发布时间】:2017-11-04 00:20:22
【问题描述】:

我正在尝试从 NVARCHAR 值中删除所有 cmets。

我不知道NVARCHAR 变量会得到哪个值,我需要删除所有以 -- 开头的 cmets,直到行尾。

例如:

-- Some Comments
SET NOCOUNT ON;

-- Some Comments

SELECT FirstName FROM dbo.Users WHERE Id = @Id;

删除 cmets 后应该如下所示:

SET NOCOUNT ON;



SELECT FirstName FROM dbo.Users WHERE Id = @Id;

在 T-SQL 中有什么简单的方法吗?

提前致谢。

【问题讨论】:

  • 一点都不简单,可能想尝试使用除 sql 之外的其他工具
  • 是所有cmets都以--开头还是/**/也有?
  • -- 不是从行首开始怎么办?如果您收到包含AND Field = 'Hi--there' 的查询怎么办?解析 cmets 并不容易。我会建议另一种工具;可以解析 SQL 并在没有 cmets 的情况下将其吐出的东西。
  • @MishaZaslavsky this 可以提供帮助
  • 我可以给你一个actual parser吗?实际目的需要 C# 知识和略微矫枉过正(一个简单的状态机也可以完成这项特定工作),但它会给您一种温暖的模糊感觉,知道您只是在删除 cmets。 (一旦你有了一个真正的解析器,你还可以在转换脚本方面做很多更酷的事情,但这是另一回事。)

标签: sql-server tsql


【解决方案1】:

使用 ngramsN4k

CREATE FUNCTION dbo.NGramsN4K
(
  @string nvarchar(4000), -- Input string
  @N      int             -- requested token size
)
/****************************************************************************************
Purpose:
 A character-level N-Grams function that outputs a contiguous stream of @N-sized tokens 
 based on an input string (@string). Accepts strings up to 4000 nvarchar characters long.
 For more information about N-Grams see: http://en.wikipedia.org/wiki/N-gram. 

Compatibility: 
 SQL Server 2008+, Azure SQL Database

Syntax:
--===== Autonomous
 SELECT position, token FROM dbo.NGramsN4K(@string,@N);

--===== Against a table using APPLY
 SELECT s.SomeID, ng.position, ng.token
 FROM dbo.SomeTable s
 CROSS APPLY dbo.NGramsN4K(s.SomeValue,@N) ng;

Parameters:
 @string  = The input string to split into tokens.
 @N       = The size of each token returned.

Returns:
 Position = bigint; the position of the token in the input string
 token    = nvarchar(4000); a @N-sized character-level N-Gram token

Developer Notes:  
 1. NGramsN4K is not case sensitive

 2. Many functions that use NGramsN4K will see a huge performance gain when the optimizer
    creates a parallel execution plan. One way to get a parallel query plan (if the 
    optimizer does not chose one) is to use make_parallel by Adam Machanic which can be 
    found here:
 sqlblog.com/blogs/adam_machanic/archive/2013/07/11/next-level-parallel-plan-porcing.aspx

 3. When @N is less than 1 or greater than the datalength of the input string then no 
    tokens (rows) are returned. If either @string or @N are NULL no rows are returned.
    This is a debatable topic but the thinking behind this decision is that: because you
    can't split 'xxx' into 4-grams, you can't split a NULL value into unigrams and you 
    can't turn anything into NULL-grams, no rows should be returned.

    For people who would prefer that a NULL input forces the function to return a single
    NULL output you could add this code to the end of the function:

    UNION ALL 
    SELECT 1, NULL
    WHERE NOT(@N > 0 AND @N <= DATALENGTH(@string)) OR (@N IS NULL OR @string IS NULL);

 4. NGramsN4K is deterministic. For more about deterministic functions see:
    https://msdn.microsoft.com/en-us/library/ms178091.aspx

Usage Examples:
--===== Turn the string, 'abcd' into unigrams, bigrams and trigrams
 SELECT position, token FROM dbo.NGramsN4K('abcd',1); -- unigrams (@N=1)
 SELECT position, token FROM dbo.NGramsN4K('abcd',2); -- bigrams  (@N=2)
 SELECT position, token FROM dbo.NGramsN4K('abcd',3); -- trigrams (@N=3)

--===== How many times the substring "AB" appears in each record
 DECLARE @table TABLE(stringID int identity primary key, string nvarchar(100));
 INSERT @table(string) VALUES ('AB123AB'),('123ABABAB'),('!AB!AB!'),('AB-AB-AB-AB-AB');

 SELECT string, occurances = COUNT(*) 
 FROM @table t
 CROSS APPLY dbo.NGramsN4K(t.string,2) ng
 WHERE ng.token = 'AB'
 GROUP BY string;

----------------------------------------------------------------------------------------
Revision History:
 Rev 00 - 20170324 - Initial Development - Alan Burstein
****************************************************************************************/
RETURNS TABLE WITH SCHEMABINDING AS RETURN
WITH
L1(N) AS
(
  SELECT 1 FROM (VALUES -- 64 dummy values to CROSS join for 4096 rows
        ($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),
        ($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),
        ($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),
        ($),($),($),($),($),($),($),($),($),($),($),($),($),($),($),($)) t(N)
),
iTally(N) AS 
(
  SELECT 
  TOP (ABS(CONVERT(BIGINT,((DATALENGTH(ISNULL(@string,''))/2)-(ISNULL(@N,1)-1)),0)))
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL))    -- Order by a constant to avoid a sort
  FROM L1 a CROSS JOIN L1 b                       -- cartesian product for 4096 rows (16^2)
)
SELECT
  position = N,                                   -- position of the token in the string(s)
  token    = SUBSTRING(@string,CAST(N AS int),@N) -- the @N-Sized token
FROM iTally
WHERE @N > 0 
-- Protection against bad parameter values
AND @N <= (ABS(CONVERT(BIGINT,((DATALENGTH(ISNULL(@string,''))/2)-(ISNULL(@N,1)-1)),0)));

您可以使用以下解决方案来解决它。这将仅限于NVARCHAR(4000),但如果您需要,我可以组合一个NVARCHAR(max) 版本。另请注意,我的解决方案会忽略以“--”开头的行,并将所有内容都抓取到“--”,其中注释更深。我不是在处理 /* this comment style */,但可以修改为这样做。

解决方案

-- sample stored proc
declare @storedproc varchar(8000) =
'-- Some Comments
SET NOCOUNT ON;

-- Some Comments

SELECT FirstName -- we only need the first name
FROM dbo.Users WHERE Id = @Id;';

--select @storedproc;

-- Solution
select cleanedProc = 
(
  select substring(item, 1, isnull(nullif(charindex('--', item),0)-1,nextPos))+br
  from
  (
    select 0 union all
    select position from dbo.ngramsN4k(@storedproc,1) 
    where token = char(10)
  ) d(position)
  cross apply (values (char(10), d.position+1,
           isnull(nullif(charindex(char(10), @storedproc, d.position+1),0),8000))
  ) p(br, startPos, nextPos)
  cross apply (values (substring(@storedproc, startPos, nextPos-startPos))) split(item)
  where item not like '--%'
  order by position
  for xml path(''), type
).value('(text())[1]', 'varchar(8000)');

之前

-- Some Comments
SET NOCOUNT ON;

-- Some Comments

SELECT FirstName -- we only need the first name
FROM dbo.Users WHERE Id = @Id;

之后

SET NOCOUNT ON;


SELECT FirstName 
FROM dbo.Users WHERE Id = @Id;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-14
    • 2019-07-04
    • 1970-01-01
    • 2014-08-10
    • 2018-11-07
    • 2012-05-13
    • 1970-01-01
    • 2011-02-03
    相关资源
    最近更新 更多