.net - sitio - Expresión regular para encontrar todos los nombres de tabla en una consulta
expresiones regulares (12)
No soy tan caliente en expresiones regulares y eso ha hecho que mi pequeña mente se derrita.
Estoy tratando de encontrar todos los nombres de las tablas en una consulta. Entonces digo que tengo la consulta:
SELECT one, two, three FROM table1, table2 WHERE X=Y
Me gustaría sacar "table1, table2" o "table1" y "table2"
Pero, ¿y si no hay una declaración de dónde? 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 esto una expresión Regex factible? ¿Estoy siendo un pleb de Regex?
(PD, esto se hará en C #, pero supongamos que no importa mucho).
¡Encontré este sitio que tiene un GRAN analizador!
bien vale la pena. Funciona un placer
Creo que sería más fácil para tokenizar la cadena y buscar palabras clave SQL que podrían vincular los nombres de la tabla. Usted sabe que los nombres seguirán de FROM
, pero podrían ser seguidos por WHERE
, GROUP BY
, HAVING
o ninguna palabra clave si están al final de la consulta.
Definitivamente no es fácil.
Considera 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 usadas en esta consulta.
RegEx no es muy bueno en esto, ya que es mucho más complicado de lo que parece:
- ¿Qué pasa si usan uniones IZQUIERDA / DERECHA INTERIOR / EXTERIOR / CRUZ / MERGIO / NATURAL en lugar de la sintaxis a, b? La sintaxis a, b debe evitarse de todos modos.
- ¿Qué pasa con las consultas anidadas?
- Qué pasa si no hay tabla (seleccionando una constante)
- ¿Qué pasa con los saltos de línea y otros formatos de espacios en blanco?
- Alias nombres?
Podría seguir.
Lo que puede hacer es buscar un analizador SQL y ejecutar su consulta a través de eso.
Todo lo dicho sobre la utilidad de dicha expresión regular en el contexto SQL. Si insistes en una expresión regular y tus sentencias SQL siempre se parecen a la que mostraste (eso significa que no hay subconsultas, uniones, etc.), podrías usar
FROM/s+([^ ,]+)(?:/s*,/s*([^ ,]+))*/s+
Llego bastante tarde a la fiesta, sin embargo, pensé que compartiría una expresión regular que estoy usando actualmente para analizar todos los objetos de nuestra base de datos y no estoy de acuerdo con el sentimiento de que no es posible hacerlo con una.
La expresión regular tiene algunas suposiciones
1) No está utilizando el estilo de sintaxis de unión A, B
2) Cualquiera que sea el analizador de expresiones regulares que estés utilizando admite ignorar el 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.
Me encanta saber de qué tipo de transacción forma parte la tabla, así que he incluido grupos de Captura con nombre para contarme.
Ahora no he utilizado la expresión regular durante mucho tiempo, por lo que probablemente haya mejoras que se puedan hacer; sin embargo, hasta ahora, en todas mis pruebas esto es exacto.
/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
Esto extraerá un nombre de tabla en una inserción En la consulta:
(?<=(INTO)/s)[^/s]*(?=/(())
Lo Siguiente hará lo mismo pero con un selecto que incluye uniones
(?<=(from|join)/s)[^/s]*(?=/s(on|join|where))
Finalmente, volviendo a una inserción si desea devolver solo los valores que se mantienen en una consulta insertada, use la siguiente Regex
(?i)(?<=VALUES[ ]*/().*(?=/))
Sé que este es un hilo viejo, pero puede ayudar a alguien más a mirar a su alrededor
Disfrutar
Intenté 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 campos de la consulta antes de analizarlas.
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;
}
En PHP, utilizo esta función, devuelve una matriz con los nombres de tabla utilizados en una declaració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();
}
Tenga en cuenta que no funciona con a, b join o schema.tablename nombrando
Espero que te sirva
Una solución es implementar una convención de nomenclatura en 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
Dividir el espacio en blanco en la matriz:
("SELECT","tbltable1.one,","tbltable1.two,","tbltable2.three","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1.one","=","tbltable2.three")
A la izquierda de los elementos al punto:
("SELECT","tbltable1","tbltable1","tbltable2","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1","=","tbltable2")
Eliminar elementos 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")
Filtrar a la izquierda 3 caracteres = "tbl"
("tbltable1","tbltable2")
Construir una expresión regular va a ser el menor de tus problemas. Dependiendo del sabor del SQL que espere soportar con este código, la cantidad de formas en que puede hacer referencia a una tabla en una declaración SQL es asombroso.
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 bases de datos 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, está trabajando con SQL Server, aquí hay un artículo sobre la detección de dependencias en esa plataforma: Encontrar dependencias en SQL Server 2005
Usé este código como una macro de Excel para analizar los nombres de tabla de selección y extracción.
Mi análisis asume que no se usa el sintax "select from a, b, c".
Simplemente ejecútelo contra su consulta SQL y si no está triste con el resultado, debe estar a solo unas pocas líneas de códigos lejos del resultado que espera. Solo depure y modifique el código en consecuencia.
Sub get_tables ()
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