2009-04-22 48 views
6

Tôi đã có một kịch bản IronPython thực hiện một loạt các câu lệnh SQL đối với cơ sở dữ liệu SQL Server. các câu lệnh là các chuỗi lớn thực sự chứa nhiều câu lệnh, được phân cách bằng từ khóa "GO". Điều đó hoạt động khi chúng được chạy từ studio quản lý sql và một số công cụ khác, nhưng không chạy trong ADO. Vì vậy, tôi chia ra các chuỗi bằng cách sử dụng 2,5 "lại" mô-đun như vậy:regex để phân tích cú pháp câu lệnh SQL

splitter = re.compile(r'\bGO\b', re.IGNORECASE) 
for script in splitter.split(scriptBlob): 
    if(script): 
     [... execute the query ...] 

này phá vỡ trong trường hợp hiếm hoi mà có từ "đi" trong một chú thích hoặc một chuỗi. Làm thế nào trong heck tôi sẽ làm việc xung quanh đó? tức là phân tích một cách chính xác chuỗi này thành hai kịch bản:

-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script') 

EDIT:

tôi đã tìm kiếm và tìm thấy tài liệu SQL này: http://msdn.microsoft.com/en-us/library/ms188037(SQL.90).aspx

Khi nó quay ra, GO phải trên dòng riêng của nó như một số câu trả lời được đề xuất. Tuy nhiên nó có thể được theo sau bởi một số nguyên "count" mà thực sự sẽ thực thi lô lệnh mà nhiều lần (có ai thực sự sử dụng trước đó ??) và nó có thể được theo sau bởi một dòng chú thích trên cùng một dòng (nhưng không phải là một nhiều đường, tôi đã kiểm tra này) vì vậy, các regex kỳ diệu sẽ giống như thế:.

"(?m)^\s*GO\s*\d*\s*$" 

Trừ này không tính đến:

  • một lời nhận xét đơn dòng có thể ("--" tiếp theo bất kỳ ký tự ngoại trừ ngắt dòng) ở cuối.
  • toàn bộ dòng nằm bên trong nhận xét nhiều dòng lớn hơn.

Tôi không quan tâm đến việc nắm bắt đối số "đếm" và sử dụng đối số. Bây giờ tôi có một số tài liệu hướng dẫn kỹ thuật, tôi gần như đang viết bài này "để làm rõ" và không bao giờ phải lo lắng về nó nữa.

+0

tôi upmodded câu trả lời của mọi người, cảm ơn vì sự giúp đỡ! Tôi đã đưa ra câu trả lời cho mcassano vì anh ấy là người đầu tiên đề xuất rằng GO luôn có thể nằm trên một dòng riêng, dẫn tôi tìm kiếm tài liệu cho lệnh đó và cuối cùng dẫn đến một giải pháp dễ dàng hơn nhiều. –

Trả lời

8

Có phải "GO" luôn luôn nằm trên một dòng không? Bạn chỉ có thể chia thành "^ GO $".

+0

sau khi bật kết hợp nhiều dòng, tôi cũng đã ném vào khoảng trống tùy chọn, chỉ trong trường hợp. –

+0

Tôi nghĩ rằng nó thường là trên một dòng riêng của nó, và đó có thể là một giải pháp đủ tốt cho kịch bản này. Mặc dù nghiêm chỉnh, điều đó sẽ không bảo vệ chống lại GO trên dòng riêng của nó bên trong một bình luận nhiều dòng hoặc một chuỗi nhiều dòng (cũng rất hiếm.) –

+0

Kịch bản cập nhật hiện tại của tôi khắc phục lỗi này. –

5

vì bạn có thể có nhận xét bên trong nhận xét, nhận xét lồng nhau, nhận xét bên trong truy vấn, v.v., không có cách nào sane để làm điều đó với regexes.

Chỉ Immagine kịch bản sau đây:

INSERT INTO table (name) VALUES (
-- GO NOW GO 
'GO to GO /* GO */ GO' + 
/* some comment 'go go go' 
-- */ 'GO GO' /* 
GO */ 
) 

Đó không nhắc đến:

INSERT INTO table (go) values ('xxx') GO 

Cách duy nhất là xây dựng một phân tích cú pháp stateful để thay thế. Một trong đó đọc một char tại một thời điểm, và có một lá cờ sẽ được thiết lập khi nó nằm trong chuỗi comment/quote-delimited/etc và thiết lập lại khi nó kết thúc, vì vậy mã có thể bỏ qua các cá thể "GO" khi ở bên trong.

+1

Một trình phân tích cú pháp sẽ là giải pháp tốt nhất, nhưng nếu bạn có thể đảm bảo rằng GO luôn luôn trên một dòng của chính bạn, bạn là khá an toàn, đặc biệt là kể từ khi SQL92 không có ý kiến ​​nhiều dòng. –

+1

bạn chỉ cần thổi tâm trí của tôi. –

5

Nếu GO luôn là trên một dòng riêng của mình, bạn có thể sử dụng phân chia như thế này:

#!/usr/bin/python 

import re 

sql = """-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 5 --this is a test 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script')""" 

statements = re.split("(?m)^\s*GO\s*(?:[0-9]+)?\s*(?:--.*)?$", sql) 

for statement in statements: 
    print "the statement is\n%s\n" % (statement) 
  • (?m) bật matchings multiline, đó là ^$ sẽ phù hợp bắt đầu và kết thúc của dòng (thay vì bắt đầu và kết thúc chuỗi).
  • ^ trận đấu vào lúc bắt đầu của một dòng
  • \s* phù hợp không hay nhiều khoảng trắng (space, tab, vv)
  • GO phù hợp với một GO đen
  • \s* trận đấu như trước
  • (?:[0-9]+)? phù hợp với một tùy chọn số nguyên (với số 0 đứng đầu có thể)
  • \s* đối sánh như trước
  • .210 phù hợp với một tùy chọn end-of-line comment
  • $ trận đấu vào cuối của một dòng

Việc phân chia sẽ tiêu thụ dòng GO, vì vậy bạn sẽ không phải lo lắng về nó. Điều này sẽ để lại cho bạn một danh sách các câu lệnh.

Phân chia được sửa đổi này có vấn đề: nó sẽ không cung cấp cho bạn số sau GO, nếu điều quan trọng tôi sẽ nói đó là thời gian để chuyển sang trình phân tích cú pháp của một số biểu mẫu.

+0

Đây là một gợi ý tốt và cảm ơn sự cố chi tiết. –

+0

Chas, xem chỉnh sửa ở trên - làm cách nào để kiểm tra một dòng chú thích ở cuối dòng GO? Xin vui lòng tha thứ cho sự thiếu kinh nghiệm của tôi. –

2

Điều này sẽ không phát hiện nếu GO từng được sử dụng làm tên biến bên trong một số tuyên bố, nhưng phải chú ý đến những người bên trong nhận xét hoặc chuỗi.

CHỈNH SỬA: Điều này hiện hoạt động nếu GO là một phần của tuyên bố, miễn là nó không thuộc dòng riêng của nó. sử dụng

import re 

line_comment = r'(?:--|#).*$' 
block_comment = r'/\*[\S\s]*?\*/' 
singe_quote_string = r"'(?:\\.|[^'\\])*'" 
double_quote_string = r'"(?:\\.|[^"\\])*"' 
go_word = r'^[^\S\n]*(?P<GO>GO)[^\S\n]*\d*[^\S\n]*(?:(?:--|#).*)?$' 

full_pattern = re.compile(r'|'.join((
    line_comment, 
    block_comment, 
    singe_quote_string, 
    double_quote_string, 
    go_word, 
)), re.IGNORECASE | re.MULTILINE) 

def split_sql_statements(statement_string): 
    last_end = 0 
    for match in full_pattern.finditer(statement_string): 
     if match.group('GO'): 
      yield statement_string[last_end:match.start()] 
      last_end = match.end() 
    yield statement_string[last_end:] 

Ví dụ:

statement_string = r""" 
-- this is a great database script! go team go! 
INSERT INTO go(go) VALUES ('go away!') 
go 7 -- foo 
INSERT INTO go(go) VALUES (
    'I have to GO " with a /* comment to GO inside a /* GO string /*' 
) 
/* 
    here are some comments that go with this script. 
    */ 
    GO 
    INSERT INTO go(go) VALUES ('this is the next script') 
""" 

for statement in split_sql_statements(statement_string): 
    print '=======' 
    print statement 

Output:

======= 

-- this is a great database script! go team go! 
INSERT INTO go(go) VALUES ('go away!') 

======= 

INSERT INTO go(go) VALUES (
    'I have to GO " with a /* comment to GO inside a /* GO string /*' 
) 
/* 
    here are some comments that go with this script. 
    */ 

======= 

    INSERT INTO go(go) VALUES ('this is the next script') 
+0

không thành công trên 'Tôi phải GO' bằng/* chú thích GO trong chuỗi/* GO/* 'GO – nosklo

+0

Không, nó sẽ không tìm thấy tất cả các kết quả trùng lặp, vì vậy toàn bộ chuỗi trích dẫn một lần –

+0

Không thất bại khi tôi chạy nó ... –

Các vấn đề liên quan