【问题标题】:Howto clean comments from raw sql file如何清除原始 sql 文件中的注释
【发布时间】:2011-08-17 19:49:02
【问题描述】:

我在清理现有 sql 文件中的 cmets 和空行时遇到问题。 该文件有超过 10k 行,因此不能手动清理它。

我有一个小 Python 脚本,但我不知道如何处理多行插入中的 cmets。

代码:

f = file( 'file.sql', 'r' )
t = filter( lambda x: not x.startswith('--') \
            and not x.isspace() 
  , f.readlines() )
f.close()
t #<- here the cleaned data should be

它应该如何工作:

这应该清洗:

-- normal sql comment

这应该保持原样:

CREATE FUNCTION func1(a integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
        -- comment
       [...]
END;
$$;

INSERT INTO public.texts (multilinetext) VALUES ('
and more lines here \'
-- part of text 
\'
[...]

');

【问题讨论】:

  • 你能记录下你通过了多少非转义引号吗?奇数表示注释是字符串的一部分,因此不应删除。
  • 但它仍然会从存储过程中删除 cmets
  • 我有点跑题了,但也许你真正的问题是你的 SQL 文件是 10K 行。或者您没有使用版本控制。或两者。像我们的流程这样的东西可能会对您有所帮助。请参阅我对stackoverflow.com/questions/5330065/…的回答
  • @Catcall 我两者都有,该文件是由几个较小的文件生成的,但是对于生产环境部署,不需要 cmets(它们大约占文件的 60%)
  • 更改生成文件可能更容易。跳过从函数和存储过程中删除 cmets 肯定更容易——只是不要将“删除 cmets”代码添加到构建函数的 make 部分(或充满函数的文件)。

标签: python sql postgresql text-parsing


【解决方案1】:

试试sqlparse 模块。

更新示例:将 cmets 留在插入值中,将 cmets 留在 CREATE FUNCTION 块中。您可以进一步调整以调整行为:

import sqlparse
from sqlparse import tokens

queries = '''
CREATE FUNCTION func1(a integer) RETURNS void
    LANGUAGE plpgsql
        AS $$
        BEGIN
                -- comment
       END;
       $$;
SELECT -- comment
* FROM -- comment
TABLE foo;
-- comment
INSERT INTO foo VALUES ('a -- foo bar');
INSERT INTO foo
VALUES ('
a 
-- foo bar'
);

'''

IGNORE = set(['CREATE FUNCTION',])  # extend this

def _filter(stmt, allow=0):
    ddl = [t for t in stmt.tokens if t.ttype in (tokens.DDL, tokens.Keyword)]
    start = ' '.join(d.value for d in ddl[:2])
    if ddl and start in IGNORE:
        allow = 1
    for tok in stmt.tokens:
        if allow or not isinstance(tok, sqlparse.sql.Comment):
            yield tok

for stmt in sqlparse.split(queries):
    sql = sqlparse.parse(stmt)[0]
    print sqlparse.sql.TokenList([t for t in _filter(sql)])

输出:

CREATE FUNCTION func1(a integer) RETURNS void
    LANGUAGE plpgsql
        AS $$
        BEGIN
                -- comment
       END;
       $$;

SELECT * FROM TABLE foo;

INSERT INTO foo VALUES ('a -- foo bar');

INSERT INTO foo
VALUES ('
a
-- foo bar'
);

【讨论】:

  • 感谢提示,但这也是从内部函数中剥离 cmets,有没有办法防止它?
  • @Szymon Lukaszczyk :我只是对@samplebias 发表同样的评论 :),如果你想要一些适用于你的示例的东西,请查看我的答案。
  • @Szymon 更新以显示原始标记,您可以通过迭代它们来过滤它们。
  • 几乎,我的示例中的 INSERT 仍然失败
  • @Szymon 我更新了它来处理这种情况。这应该提供足够的基线,您可以扩展和修改。
【解决方案2】:

添加更新的答案:)

import sqlparse

sql_example = """--comment
SELECT * from test;
INSERT INTO test VALUES ('
-- test
a
');
 """
print sqlparse.format(sql_example, strip_comments=True).strip()

输出:

SELECT * from test;
INSERT INTO test VALUES ('
-- test
a
');

它实现了相同的结果,但也涵盖了所有其他极端情况并且更简洁

【讨论】:

    【解决方案3】:

    这是与您的示例一起使用的 samplebias 答案的扩展:

    import sqlparse
    
    sql_example = """--comment
    SELECT * from test;
    INSERT INTO test VALUES ('
    -- test
    a
    ');
    """
    
    new_sql = []
    
    for statement in sqlparse.parse(sql_example):
        new_tockens = [stm for stm in statement.tokens 
                       if not isinstance(stm, sqlparse.sql.Comment)]
    
        new_statement = sqlparse.sql.TokenList(new_tockens)
        new_sql.append(new_statement.to_unicode())
    
    print sqlparse.format("\n".join(new_sql))
    

    输出:

    SELECT * from test;
    
    INSERT INTO test VALUES ('
    -- test
    a
    ');
    

    【讨论】:

    • print(sqlparse.format("\n".join(new_sql))) 将示例传入 python 后返回u'\n'
    • @Szymon Lukaszczyk:我直接从脚本中粘贴了上面的代码,该脚本产生了与我的答案相同的输出奇怪??!! :)
    • 得到(, , )而不是正确的对象.不知道为什么。
    • 我已经从 debian sid repo 安装了 sqlparse,(版本 0.1.2-2)
    • @Szymon Lukaszczyk:我的 ubuntu 机器上有相同的版本,你说你得到的输出是 sqlparse.parse(sql_example) 奇怪的返回值!!??
    【解决方案4】:

    可以用正则表达式来做到这一点。首先,您必须按字符串拆分文件,然后您可以按 cmets 拆分文件。下面的 Perl 程序可以做到这一点:

    #! /usr/bin/perl -w
    
    # Read hole file.
    my $file = join ('', <>);
    
    # Split by strings including the strings.
    my @major_parts = split (/('(?:[^'\\]++|\\.)*+')/, $file);
    
    foreach my $part (@major_parts) {
        if ($part =~ /^'/) {
            # Print the part if it is a string.
            print $part; 
        }
        else {
            # Split by comments removing the comments
            my @minor_parts = split (/^--.*$/m, $part);
            # Print the remaining parts.
            print join ('', @minor_parts);
        }
    }
    

    【讨论】:

    • 他希望将 cmets 保留在函数/程序内部,并过滤掉其余部分。
    【解决方案5】:
    # Remove comments i.e. lines beginning with whitespace and '--' (using multi-line flag)
    re.sub('^\s*--.*\n?', '', query, flags=re.MULTILINE)
    

    正则表达式字符串解释:

    • ^ 行首
    • \s 空格
    • \s* 零个或多个空白字符
    • -- 两个连字符(静态字符串模式)
    • .* 零个或多个任意字符(即该行的其余部分)
    • \n 换行符
    • ?字符串结束
    • flags = re.M 是多行修饰符

    "指定时,模式字符'^'匹配字符串的开头和每行的开头(紧跟在每个换行符之后)"

    有关详细信息,请参阅 Python 正则表达式文档:

    https://docs.python.org/3/library/re.html

    【讨论】:

    • 请添加一些解释
    • 最好也包含 /* */ cmets,但我猜这将很难适应 -- 可能重叠的行或以 -- 开头的行并结束与 */ 并且您错过了结束部分。
    猜你喜欢
    • 2020-01-03
    • 1970-01-01
    • 2016-03-27
    • 1970-01-01
    • 2015-10-04
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    相关资源
    最近更新 更多