mboost-dp1

C# Regex til at finde SQL-queries


Gå til bund
Gravatar #1 - Tang
26. sep. 2010 20:32
Jeg forsøger at lede kildekoden til en Stored Procedure igennem og trække alle SQL-queries ud af denne. Jeg er dog ikke interesseret i det, der står efter WHERE-delen, da jeg kun skal bruge navnet på de tabeller der refereres til.


Regex r = new Regex("select(.+?)from(.+?)(where|end|if|else|select|delete|update)", RegexOptions.IgnoreCase);

Match = r.Match(str_source);



Problemet er denne del:
(where|end|if|else|select|delete|update)

Grunden til at bruge det er, at det er de måder man kan finde afslutningen på en query - Problemet er bare at hvis en query afsluttes fordi en ny select startes så får man ikke den næste select med, da den jo var en del af den første match.

Nogen der kan hjælpe mig eller henvise mig til en bedre løsning?
Gravatar #2 - arne_v
26. sep. 2010 21:26
#1

Du kunne starte med at splitte op i sætninger (adskilt af semikolon) og processe dem en ad gangen.

Derudover skal du nok have lidt mere for at finde tabelnavnene udfra FROM expression, der jo både kan være old style og nye JOIN style.

Og så er der spørgsmålet om hvordan du vil håndtere diverse subquery syntaxer.
Gravatar #3 - Tang
27. sep. 2010 07:15
#2

Jeg ved godt at der er en del at tage højde for. Det er dog også derfor at mit første forsøg er bare at trække alle select-statements ud. Subqueries vil jo så også komme med da det jo også er select-statements.
Gravatar #4 - arne_v
27. sep. 2010 19:31
#3

Du mener at den Regex kan klare:

SELECT t1.f1,(SELECT f2 FROM t2 WHERE f3=7)
FROM t1 JOIN (SELECT * FROM t3 WHERE f4 > 7) x ON t1.5=x.f5
WHERE t1.f6 = (SELECT MAX(f6) FROM t4 WHERE t1.f7=t4.f7)

?
Gravatar #5 - Tang
28. sep. 2010 09:00
#4

Som sagt er det bare en start.

Hvis du læser mit oprindelige spørgsmål vil du se at jeg spørger om, hvordan jeg får den næste select med (da den jo også afslutter den forrige).

I mellemtiden har jeg dog fundet ud af at man kan trække alle SQL-queries ud af en stored procedure ved hjælp af dens execution plan. Så det løser da en lille del af mit problem...
Gravatar #6 - Windcape
28. sep. 2010 09:25
#5

Hvorfor er det lige at i har en stored procedure med så mange og så store queries i, at det ikke kan overskues som manuel operation?

Det skal jo være ret meget data før det kan betale sig at skrive et program til formålet.
Gravatar #7 - Tang
28. sep. 2010 11:35
Det drejer sig ikke kun om én stored procedure.

Jeg skal indeksere over 2000 stored procedures og identificere hvilke tabeller (og views) de bruger/er afhængige af...
Gravatar #8 - Windcape
28. sep. 2010 11:47
#7

Ville det ikke være nemmere at tage en liste af samtlige mulige tabelnavne, og så tælle antal unikke forekomster i hver stored procedure?

Gravatar #9 - Tang
28. sep. 2010 11:53
#8

Det var skam også min første idé

Problemet er bare, at der er over 500 sammenfald mellem tabel- og felt-navne i databasen...
Gravatar #10 - Windcape
28. sep. 2010 11:57
#9

I så fald ville jeg nok kigge på at køre samtlige stored procedures (måske på en klon af database strukturen / virtuel maskine), og så aflæse hvilke tabeller der er blevet kørt kode på via. en profiler.

Har du prøvet med SQL Query Analyzer?

http://msdn.microsoft.com/en-us/library/aa216945(S...
Gravatar #11 - Tang
28. sep. 2010 15:35
Ud over, at jeg har kildekoder til alle SP'er har jeg dog også en database med bla. alle SP'erne i. Dette er dog en prokuktions-database, så jeg kan ikke rigtig afvikle dem.

Til gengæld kan jeg afvikle følgende kode og få execution plan for en vilkårlig SP.

set showplan_xml on
GO
exec min_sp


Så kan jeg få navne på berørte tabeller og returneret samtlige queries i SP'en.

Så jeg skal egentlig bare kunne trække tabel-navne ud af en vilkårlig query (både select, delete og update).
Gravatar #12 - arne_v
29. sep. 2010 02:46
#5

Du skal bare gøre dig klart om du kan nøjes med en 99% korrekt løsning eller har brug for en 100% korrekt løsning.

Regex er velegnet til 99% ikke så velegnet til 100%.
Gravatar #13 - arne_v
29. sep. 2010 02:51
#11

En mulighed for at få det rimeligt rigtigt var at bruge en SQL grammatik og en parser generator.

Antlr har tilsyneladende grammatikker for både ISO SQL og SQLServer 2000 SELECT statements. Og den kan bruges med C#.
Gravatar #14 - mstify
29. sep. 2010 06:05
#1: Nu skal jeg ikke kunne sige hvor let det er at bruge en SQL grammatik og parser generator, hvis det er nemt så er det selvfølgelig oplagt.

Men ellers tror jeg sådanset ikke at den her opgave kræver en fullblown parser, mit gæt er at man ville kunne løse det her med en simpel state parser der følger de grundlæggende regler for statement-opbygnig i SQL, men drop regex det giver dig kun problemer her. Pointen er at du kun skal bruge 5% af en fullblown SQL parser og det gør det faktisk realitisk at skrive den del selv på et par timer.
Gravatar #15 - arne_v
29. sep. 2010 14:26
#14

SQL kan godt være lidt krøllet syntax mæssigt.

Jeg tror at det godt kunne blive lidt omfattende at lave en parser helt fra bunden af.
Gravatar #16 - Tang
6. okt. 2010 06:38
Jeg har fundet frem til noget kaldet TSql100Parser, som er en .NET-klasse til at parse SQL. Dokumentationen er dog rimelig dårlig, så ville lige spørge om det er vejen frem....
Gravatar #17 - arne_v
7. okt. 2010 23:02
#problemet

Nu faldt jeg lige tilfældigvis over noget!

:-)

Prøv og kig på:

SP_DEPENDS 'navn på din SP'

og:

SELECT DISTINCT referenced_schema_name,referenced_entity_name,referenced_minor_name
FROM sys.dm_sql_referenced_entities('navn på din SP','OBJECT')
Gravatar #18 - Tang
9. okt. 2010 10:01
#17

Tak for svaret. Jeg har dog læst flere steder at sp_depends ikke er 100% pålidelig.

Den anden løsning har jeg ikke lige prøvet, eller læst om før...
Gravatar #19 - gnаrfsan
9. okt. 2010 12:26
Trident (1) skrev:
(.+?)

Hvorfor .+? i stedet for .*
Gravatar #20 - arne_v
9. okt. 2010 14:50
#19

.+? er 1..mange tegn non greedy
.* er 0..mange tegn greedy

der er pæn forskel
Gravatar #21 - arne_v
9. okt. 2010 14:51
#18

Den kan nok nemt være mere pålidelig end noget hjemmebrygget!
Gravatar #22 - gnаrfsan
10. okt. 2010 19:54
arne_v (20) skrev:
der er pæn forskel

Komplet ligegyldigt, når der er noget at matche på begge sider og til det formål, så vidt jeg kan se...
Han matcher jo også 0 tegn, når han har ? med.
Gravatar #23 - arne_v
10. okt. 2010 20:44
#22

Nej - i den sammnhæng betyder den non-greedy!
Gravatar #24 - arne_v
10. okt. 2010 20:48
#22

Prøv:


using System;
using System.Text.RegularExpressions;

namespace E
{
public class Program
{
public static void Test(string re1, string s1)
{
Regex re = new Regex(re1);
Match m = re.Match(s1);
Console.WriteLine(re1 + " " + s1 + " : " + m.Success + (m.Success ? (" " + m.Value) : ""));
}
public static void Main(string[] args)
{
string[] re = { "x.+y", "x.+?y", "x.?y", "x.*y" };
string[] s = { "xy", "x1y", "x12y", "x1yx2y" };
foreach(string re1 in re)
{
foreach(string s1 in s)
{
Test(re1,s1);
}
}
Console.ReadKey();
}
}
}
Gravatar #25 - Tang
11. okt. 2010 09:56
Tak for de mange svar!

Hvis jeg må have lov til at vende tilbage til mit originale spørgsmål.

slutningen" på mit regex-pattern (slutningen kunne evt. være ordet "select"), bliver jo en del af den næste query i SP'en.

Det betyder jo så at den næste query IKKE kommer med i min regex-match...

Hvordan klarer jeg det?
Gravatar #26 - gnаrfsan
11. okt. 2010 17:48
arne_v (23) skrev:
Nej - i den sammnhæng betyder den non-greedy!

Jeg siger ikke at du ikke kan opstille et eksempel, hvor det har betydning, men det har det ikke her.
Gravatar #27 - arne_v
11. okt. 2010 20:13
#26

I den angivne regex i #1 betyder ? non-greedy uanset eksempel eller ej.

gnarfsan (22) skrev:
Han matcher jo også 0 tegn, når han har ? med.


Er forkert.

Fordi en SP kan indeholde mere end en query og fordi der kan optræde sub select i where delen, så er non-greedy absolut nødvendigt til dette formål.
Gravatar #28 - gnаrfsan
12. okt. 2010 18:13
arne_v (27) skrev:
Fordi en SP kan indeholde mere end en query og fordi der kan optræde sub select i where delen, så er non-greedy absolut nødvendigt til dette formål.

Undskyld, jeg havde fejllæst noget af indlæg 1.
Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login