2008-11-11 13 views
6

No soy tan caliente en expresiones regulares y ha hecho que mi pequeña mente se derrita.Expresión regular para encontrar todos los nombres de tabla en una consulta

Estoy tratando de encontrar todos los nombres de tablas en una consulta. Así que decir que tengo la consulta:

SELECT one, two, three FROM table1, table2 WHERE X=Y 

quisiera sacar "tabla1, tabla2" o "tabla1" y "tabla2"

Pero ¿y si no hay donde comunicado. Podría ser el final del archivo, o podría haber un grupo por o un pedido, etc. Sé que "la mayoría" de las veces esto no será un problema, pero no me gusta la idea de codificar para "la mayoría" situaciones y sabiendo que he dejado un agujero que podría hacer que las cosas salgan mal más tarde.

¿Es esta una expresión Regex factible? ¿Estoy siendo un pleb de Regex?

(P.S. esto se hará en C# pero supongo que no importa mucho).

+0

las expresiones regulares son th El menor de sus problemas. Solo enumerar todas las formas en que una tabla puede aparecer en una declaración de SQL es un problema complejo. Por cierto. Nunca mencionaste qué sabor de SQL estás tratando de analizar. – JohnFx

+0

Ni cuál es el problema subyacente que está tratando de resolver. –

+0

No creo que la expresión regular sea la solución correcta, necesita un analizador SQL en su lugar, consulte este artículo: http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use- general-sql-parser/get-columns-and-tables-in-sql-script-net-version/ –

Respuesta

13

RegEx no es muy bueno en esto, ya que es mucho más complicado de lo que parece:

  • Lo que si utilizan IZQUIERDA/DERECHA INTERIOR/EXTERIOR/CRUZ/fusionar/natural se une en lugar de la una , b sintaxis? La sintaxis a, b debe evitarse de todos modos.
  • ¿Qué ocurre con las consultas anidadas?
  • ¿Qué sucede si no hay una tabla (seleccionando una constante)
  • ¿Qué ocurre con los saltos de línea y otros formatos de espacio en blanco?
  • Nombres de alias?

Podría seguir.

Lo que puede hacer es buscar un analizador de sql y ejecutar su consulta a través de eso.

+0

Creo que el asesino de verdad va a ser views. No va a haber una forma práctica de analizar los nombres de las tablas subyacentes de las vistas incluidas en la consulta. – JohnFx

1

Definitivamente no es fácil.

Considere la posibilidad de subconsultas.

select 
    * 
from 
    A 
    join (
    select 
     top 5 * 
    from 
     B) 
    on B.ID = A.ID 
where 
    A.ID in (
    select 
     ID 
    from 
     C 
    where C.DOB = A.DOB) 

Hay tres tablas utilizadas en esta consulta.

1

Creo que sería más fácil para tokenizar la cadena y buscar las palabras clave SQL que podrían vincular los nombres de la tabla. Usted sabe que los nombres seguirán a FROM, pero podrían ir seguidos por WHERE, GROUP BY, HAVING, o ninguna palabra clave si están al final de la consulta.

4

Todo se dice acerca de la utilidad de dicha expresión regular en el contexto de SQL. Si insiste en una expresión regular y las sentencias SQL siempre se ven como el que mostró (que significa que no hay subconsultas, combinaciones, etc.), se puede usar

FROM\s+([^ ,]+)(?:\s*,\s*([^ ,]+))*\s+ 
3

Llego bastante tarde a la fiesta, sin embargo, pensé que podría compartir una expresión regular que estoy usando actualmente para analizar todos nuestros objetos de base de datos y no estoy de acuerdo con la idea de que no es posible hacerlo con una.

La expresión regular tiene algunas suposiciones

1) no está utilizando la A, B unirse estilo de sintaxis

2) Sea cual sea analizador de expresiones regulares que está usando admite ignoran caso.

3) Está analizando, selecciona, une, actualiza, elimina y trunca. No es compatible con el MERGE/NATURAL antes mencionado porque no los usamos, sin embargo, estoy seguro de que no sería difícil agregar más soporte.

Estoy interesado en saber de qué tipo de transacción forma parte la tabla, así que he incluido grupos de captura de nombres para contarme.

Ahora no he utilizado regex durante mucho tiempo, por lo que es probable que haya mejoras que se pueden hacer, sin embargo, hasta ahora en todas mis pruebas esta es precisa.

\bjoin\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bfrom\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bupdate\s+(?<Update>[a-zA-Z\._\d]+)\b|\binsert\s+(?:\binto\b)?\s+(?<Insert>[a-zA-Z\._\d]+)\b|\btruncate\s+table\s+(?<Delete>[a-zA-Z\._\d]+)\b|\bdelete\s+(?:\bfrom\b)?\s+(?<Delete>[a-zA-Z\._\d]+)\b 
0

Construir una expresión regular va a ser el menor de sus problemas. Dependiendo del sabor de SQL que espere soportar con este código, la cantidad de formas en que puede hacer referencia a una tabla en una declaración de SQL es asombrosa.

ADEMÁS, si la consulta incluye una referencia a una vista o UDF, la información sobre qué tablas subyacentes ni siquiera estará en la cadena, por lo que es completamente impráctico obtener esa información analizándola. Además, debe ser inteligente para detectar tablas temporales y excluirlas de sus resultados.

Si debe hacer esto, un mejor enfoque sería hacer uso de las API para el motor de base de datos particular para el que fue diseñado el SQL. Por ejemplo, podría crear una vista basada en la consulta y luego usar la API del servidor de aplicaciones para detectar dependencias para esa vista. El motor de DB podrá analizarlo mucho más confiablemente que nunca sin un esfuerzo enorme de ingeniería inversa del motor de consulta.

Si, por casualidad, se está trabajando con SQL Server, aquí está un artículo acerca de la detección de dependencias en esa plataforma: Finding Dependencies in SQL Server 2005

0

Esto sacará un nombre de tabla en una consulta INSERT INTO:

(?<=(INTO)\s)[^\s]*(?=\(()) 

la siguiente hará lo mismo pero con un selecto incluyendo une

(?<=(from|join)\s)[^\s]*(?=\s(on|join|where)) 

Finalmente volviendo a una inserción si desea volver Sólo los valores que se mantienen en una consulta de inserción utilizan la siguiente expresión regular

(?i)(?<=VALUES[ ]*\().*(?=\)) 

Sé que esto es un hilo viejo, pero pueden ayudar a alguien más mirando alrededor

Disfrute

0

Probé todo lo anterior pero ninguno funcionó ya que uso una amplia variedad de consultas. Sin embargo, estoy trabajando con PHP y usé una biblioteca PEAR llamada SQL_Parser, pero espero que mi solución me ayude.Además, estaba teniendo problemas con los apóstrofes y las secuencias reservadas de MySQL, así que decidí quitar todas las secciones de los campos de la consulta antes de analizarla.

function getQueryTable ($query) { 
    require_once "SQL/Parser.php"; 
    $parser = new SQL_Parser(); 
    $parser->setDialect('MySQL'); 

    // Stripping fields section 
    $queryType = substr(strtoupper($query),0,6);    
    if($queryType == 'SELECT') { $query = "SELECT * ".stristr($query, "FROM"); } 
    if ($havingPos = stripos($query, 'HAVING')) { $query = substr($query, 0, $havingPos); } 


    $struct = $parser->parse($query); 

    $tableReferences = $struct[0]['from']['table_references']['table_factors']; 

    foreach ((Array) $tableReferences as $ref) { 
     $tables[] = ($ref['database'] ? $ref['database'].'.' : $ref['database']).$ref['table']; 
    } 

    return $tables; 

} 
0

En PHP, utilizo esta función, devuelve una matriz con los nombres de las tablas utilizadas en una instrucción SQL:

function sql_query_get_tables($statement){ 
    preg_match_all("/(from|into|update|join) [\\'\\´]?([a-zA-Z0-9_-]+)[\\'\\´]?/i", 
      $statement, $matches); 
    if(!empty($matches)){ 
     return array_unique($matches[2]); 
    }else return array(); 
} 

en cuenta que no funciona con a, b se une o esquema. nombredetabla nombrar

espero que funcione para usted

1

una solución consiste en aplicar una convención de nombres de tablas y vistas. Entonces la declaración de SQL se puede analizar en el prefijo de denominación.

Por ejemplo:

SELECT tbltable1.one, tbltable1.two, tbltable2.three FROM tbltable1 INNER JOIN tbltable2 ON tbltable1.one = tbltable2.three

espacios en blanco Split a array:

("SELECT","tbltable1.one,","tbltable1.two,","tbltable2.three","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1.one","=","tbltable2.three")

conseguir a la izquierda de los elementos de período:

("SELECT","tbltable1","tbltable1","tbltable2","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1","=","tbltable2")

elementos retirar con símbolos:

("SELECT","tbltable1","tbltable1","tbltable2","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1","tbltable2")

Reducir a valores únicos:

("SELECT","tbltable1","tbltable2","FROM","INNER","JOIN","ON")

Filtro en Left 3 caracteres = "TBL"

("tbltable1","tbltable2")

0

He utilizado este codificar como una macro de Excel para analizar la tabla de selección y extracción e nombres.

Mi análisis asume que la sintaxis “seleccionar de a, b, c" no se utiliza.

Sólo ejecutarlo contra la consulta SQL y si usted no está sadisfied con el resultado que debe ser sólo unas pocas líneas de los códigos de distancia a partir del resultado que espera. Sólo depurar y modificar el código en consecuencia.

get_tables Sub()

sql_query = Cells(5, 1).Value 

tables = "" 

'get all tables after from 

sql_from = sql_query 

While InStr(1, UCase(sql_from), UCase("from")) > 0 

    i = InStr(1, UCase(sql_from), UCase("from")) 

    sql_from = Mid(sql_from, i + 5, Len(sql_from) - i - 5) 

    i = InStr(1, UCase(sql_from), UCase(" ")) 

    While i = 1 

     sql_from = Mid(sql_from, 2, Len(sql_from) - 1) 

     i = InStr(1, UCase(sql_from), UCase(" ")) 

    Wend 

    i = InStr(1, sql_join, Chr(9)) 

    While i = 1 

     sql_join = Mid(sql_join, 2, Len(sql_join) - 1) 

     i = InStr(1, sql_join, Chr(9)) 

    Wend 

    a = InStr(1, UCase(sql_from), UCase(" ")) 

    b = InStr(1, sql_from, Chr(10)) 

    c = InStr(1, sql_from, Chr(13)) 

    d = InStr(1, sql_from, Chr(9)) 

    MinC = a 

    If MinC > b And b > 0 Then MinC = b 

    If MinC > c And c > 0 Then MinC = c 

    If MinC > d And d > 0 Then MinC = d 

    tables = tables + "[" + Mid(sql_from, 1, MinC - 1) + "]" 

Wend 

'get all tables after join 

sql_join = sql_query 

While InStr(1, UCase(sql_join), UCase("join")) > 0 

    i = InStr(1, UCase(sql_join), UCase("join")) 

    sql_join = Mid(sql_join, i + 5, Len(sql_join) - i - 5) 

    i = InStr(1, UCase(sql_join), UCase(" ")) 

    While i = 1 

     sql_join = Mid(sql_join, 2, Len(sql_join) - 1) 

     i = InStr(1, UCase(sql_join), UCase(" ")) 

    Wend 

    i = InStr(1, sql_join, Chr(9)) 

    While i = 1 

     sql_join = Mid(sql_join, 2, Len(sql_join) - 1) 

     i = InStr(1, sql_join, Chr(9)) 

    Wend 

    a = InStr(1, UCase(sql_join), UCase(" ")) 

    b = InStr(1, sql_join, Chr(10)) 

    c = InStr(1, sql_join, Chr(13)) 

    d = InStr(1, sql_join, Chr(9)) 

    MinC = a 

    If MinC > b And b > 0 Then MinC = b 

    If MinC > c And c > 0 Then MinC = c 

    If MinC > d And d > 0 Then MinC = d 

    tables = tables + "[" + Mid(sql_join, 1, MinC - 1) + "]" 

Wend 

tables = Replace(tables, ")", "") 

tables = Replace(tables, "(", "") 

tables = Replace(tables, " ", "") 

tables = Replace(tables, Chr(10), "") 

tables = Replace(tables, Chr(13), "") 

tables = Replace(tables, Chr(9), "") 

tables = Replace(tables, "[]", "") 

End Sub

Cuestiones relacionadas