PDA

Ver la Versión Completa : Tabla que llena un query


eduarcol
08-03-2008, 16:46:52
Hola a todos, estoy trabajando con los componentes ZEOS, base de datos Firebird y Delphi 7. Me conecto por medio de un Query pero este query es muy dinamico, puede ser utilizado para mostrar informacion de cualquier tabla, aqui viene el problema: Yo puedo recuperar todos los campos que devuelve la consulta por medio del FieldDefs, lo que me falta saber es por medio de que propiedad puedo recuperar la tabla o tablas que llena esta Query?

Gracias por leerme

yusnerqui
08-03-2008, 18:16:45
La query está creada en la base de datos o la generas tú??, estoy pensando en parsear el texto de la propiedad sql.text y de ahí sacar las tablas

gluglu
08-03-2008, 18:55:18
Aux_SelectSQL1 := '';
Aux_SelectSQL2 := UpperCase((DataSet as TIBDataSet).SelectSQL.Text);

for j := 1 to Length(Aux_SelectSQL2) do begin
if Aux_SelectSQL2[j] = #13 then
Aux_SelectSQL1 := Aux_SelectSQL1 + ' '
else
if Aux_SelectSQL2[j] <> #10 then
Aux_SelectSQL1 := Aux_SelectSQL1 + Aux_SelectSQL2[j];
end;

Aux_Pos := 1;
while PosEx(' FROM', Aux_SelectSQL1, Aux_Pos) <> 0 do begin
Aux_Pos := PosEx(' FROM', Aux_SelectSQL1, Aux_Pos) + 1;
end;

Dec(Aux_Pos);
Aux_Table := Copy(Aux_SelectSQL1, Aux_Pos+6, PosEx(' ', Aux_SelectSQL1, Aux_Pos+6) - (Aux_Pos+6));

El nombre de la tabla lo tendrás finalmente en Aux_Table.

;)

P.D. He obviado el código correspondiente a la declaración de variables.

eduarcol
08-03-2008, 19:30:56
Pues lo que pensaron ustedes es como lo tengo en estos momentos, solo queria asegurar que no hubiese una propiedad del dataset que guarde la lista de las tablas involucradas

cTablas := Copy(sq.SQl.Text, Pos('from', sq.SQl.Text) + 4, Length(sq.SQl.Text));
cTablas := Copy(cTablas, 1, Pos('where', cTablas) - 1);

ahora con el monstruo de codigo que coloco gluglu, me puse a sudar, para que es la comparacion con #13 o #10??

gluglu
09-03-2008, 11:27:15
Al menos yo, utilizo en algunos casos sentencias SQL muy largas que por supuesto detallo en varias líneas, y estas líneas incluyen entonces un #13#10 para salto de línea.

Es por ello que para parsear la sentencia SQL correspondiente al Select elimino previamente esos saltos de línea y los convierto en blancos (' ') para obtener un único string a parsear posteriormente.

Y quiero añadir que tu código no sería correcto en varios casos :o :

1. Que en algún momento decidieras hacer uso de mayúsculas/minúsculas en tu texto SQL. Por eso convierto previamente todo a caracteres mayúsculas con Upper. No recuerdo en estos momentos excatamente si en caso de no hacerlo así, y dejando los saltos de línea, las funciones Copy, Pos y PosEx devuelven valores con sentido. Supongo que en su momento lo analizé y ví que era así, que dichas funciones devuelven valores raros si por medio hay #13#10 de saltos de línea.

2. Que alguno de tus campos de la tabla haya sido nombrado con algun nombre que contenga 'from' como parte del nombre. (Ej. : se me ocurre ahora mismo la palabra 'fromage', que significa queso en francés).

Select fromage from mi_tabla_de_fromages

En este caso tu código detectaría la 1ª aparición de 'from' en los nombres de los campos de la tabla y no el nombre de tabla que estás buscando.

Es por ello que deberías buscar la última aparición de ' from', con un espacio delante, ya que si 'from' está a su vez contenido en el propio nombre de la tabla, si no buscas con el espacio delante, tampoco obtendrás el nombre de la tabla.

Por ello incluí el bucle expresado en mi código utilizando PosEx.

3. Tu código tampoco funcionaría correctamente si utilizas un 'alias' para el nombre de la tabla.

Select fromage from mi_tabla_de_fromages mtdf where fromage = 1

(je je ... me acabo de dar cuenta que mi código tampoco funciona 100% :cool: ... Ahora después me pondré a corregirlo y tener en cuenta las apariciciones de ' from' después de un where :().

No debes de buscar el nombre de la tabla hasta el próximo 'where' porque puede haber un alias en medio. Deberás buscar desde la correcta aparición de ' from' hasta el siguiente espacio en blanco para ignorar el alias.

Además, por supuesto que se pueden dar multitud de ocasiones de sentencias SQL que no contengan ninguna condición 'where', por lo que dicha palabra ni siquiera aparecerá en la sentencia SQL y tu código tampoco sería correcto.

En cualquier caso, también habría que tener en cuenta que 'where' también pudiera formar parte de cualquier nombre de campo.


Como verás, finalmente no es asunto tan fácil como cabría pensar. :rolleyes:

Espero no haberte 'desmoralizado' con las múltiples posibilidades de fallo de tu código. :p :p :p

Lepe
09-03-2008, 11:55:58
Aquí un humilde TSelectString para crear al vuelo y modificar los sqls.

Se trata de trocear un Sql en Secciones ( el select, el from, el where, etc), poder acceder a cada una de ellas y modificarlas con algo de comodidad.


var ss: TSelectString;
begin
ss := TSelectString.Create;
ss.text := 'select * from tabla where nombre = ' + edit2.text;

if BuscaApellido then
ss.AddToWhere(oAnd, 'apellido = '+edit1.text);

if ordenaApellido then
ss.AddToSection(pOrderBy, 'Apellido Desc');


ShowMessage(ss.Text);
query1.sql.text := ss.text;
ss.Free;
query1.open;


ss.Sections[pFrom] --> te devolverá desde la palabra "FROM " hasta el "where " (o el final del sql);

No es una versión definitiva, pero algo es algo.

Edito:
- hay algunas unidades que no las tendréis, borrarla del uses, no las utiliza.
- "saltolinea" es una constante definida como const saltolinea = #13#10
- Creo eso es todo....


Saludos

gluglu
09-03-2008, 11:56:20
Ya creo que lo tengo correctamente :

Aux_SelectSQL1 := '';
Aux_SelectSQL2 := UpperCase((DataSet as TIBDataSet).SelectSQL.Text);

for j := 1 to Length(Aux_SelectSQL2) do begin
if Aux_SelectSQL2[j] = #13 then Aux_SelectSQL1 := Aux_SelectSQL1 + ' '
else
if Aux_SelectSQL2[j] <> #10 then
Aux_SelectSQL1 := Aux_SelectSQL1 + Aux_SelectSQL2[j];
end;

Aux_Pos := 1;
while PosEx(' FROM ', Aux_SelectSQL1, Aux_Pos) <> 0 do begin
Aux_Pos := PosEx(' FROM ', Aux_SelectSQL1, Aux_Pos) + 1;
end;

Dec(Aux_Pos);
Aux_Table := Copy(Aux_SelectSQL1, Aux_Pos+6, PosEx(' ', Aux_SelectSQL1, Aux_Pos+6) - (Aux_Pos+6));

Bastaba con buscar 'from' como palabra 'reservada'. Es decir, basta con buscar ' from ' con espacio delante y detrás.

Y revisando el código, me dí cuenta de otra particularidad que no mencioné anteriormente : las subconsultas !

Select fromage, (Select loquesea from mi_otra_tabla where loquesea = 1) from mi_tabla_de_fromages mtdf where fromage = 1

Por eso lo de localizar la última aparición de ' from ' en la sentencia SQL. ;)

Saludos :)

Lepe
09-03-2008, 12:11:34
Tienes razón, el TSelecStrings no tiene en cuenta las subconsultas (yo no las uso mucho) ;).

Ahora que lo pienso:

select nombre, (select distinct edad from edades)
from tabla
where edad < (select edad from edades)


... amos, que si nos ponemos a buscarle fallos, no terminamos.

Por suerte y sin tenerlo programado, puedes armar la consulta como:

ss.Sections[pSelect] := 'nombre, (select distinct edad from edades)';
ss.Sections[pFrom] := 'tabla';
ss.AddToWhere(oNull, 'edad < (select edad from edades)');


Por cierto guglu, deja de chatear conmigo :p

Saludos

eduarcol
09-03-2008, 15:54:15
Hola y gracias por la ayuda, me ha servido de mucho, no me desmoraliza las fallas ya habia analizado una serie de escenarios y se que no funcionaria a la perfeccion. Por eso buscaba alguna propiedad magica, que me delvoviera las tablas involucradas.

Yo creo una pantalla de busqueda generica: Una caja de texto, un Grid, un boton y un query, en la inicializacion del form paso como parametro la sentencia que quiero mostrar y automaticamente se arma el grid con la informacion resultante de la consulta. Al darle el boton aceptar devuelve el valor del campo en la posicion 0.

Ahorita doy la opcion con un filtro de ir buscando el registro que coincida con lo que se escribe en la caja de texto. Pero como se quiere acceder por medio de internet es muy lento debido a que hay que cargar todos los registros. Lo que pretendo es relanzar la consulta que lleno la tabla con los filtros establecidos por el usuario. Es decir, la primera pudo haber sido por el nombre, luego por el apellido, pense hacerlo como algo parecido a como sugirio Lepe, tres variables: Campos, Tablas y condicion y armar el sql algo asi:

'Select ' + Campos + ' from ' + Tablas + ' where ' + Condicion

pero tambien le doy a los usuarios avanzados la capacidad de crear sus propias consultas sql.

Bueno espero haberme explicado y usare las sugerencias obtenidas. Muchas gracias a todos por sus acertadas participaciones.

Saludos

Caral
09-03-2008, 16:47:48
Hola
No se si con zeos se podrá hacer esto (http://caral.clubdelphi.com/Publico/Ejemplos/Access.zip), pero Roman me enseño a hacer un filtro de selección por tablas y campos, revisalo, tal vez te de ideas.
Saludos

eduarcol
09-03-2008, 16:53:42
Algo parecido estoy haciendo amigo Carlos, pero no puedo trabajar con Filtros porq al traer todos los registros por internet se haria insorpotable la lentitud al abrir la pantalla de busqueda.

eduarcol
27-03-2008, 22:17:02
A ver, apoyandome en la idea de GluGlu resolvi el problema, aqui esta el codigo para el que le interese, basicamente lo que hace es qe toma la ultima aparicion del WHERE y divide el string en dos, luego inserta el filtro y lo vuelve a unir. Pronto lo subire a mi subdominio para el que lo necesite

//si el control activo es caja de texto Genera el filtro en la consulta
//Divide la sentencia SQL
sq.DisableControls;

sq.Active := False;

//Reestablece la sentencia original
sq.SQL.Text := cSqlOriginal;

//Inicializa las variables
Aux_SelectSQL1 := '';
Aux_SelectSQL2 := UpperCase(sq.SQL.Text);

//Elimina los saltos de linea y saca como resultado Aux_SelectSQL1
for j := 1 to Length(Aux_SelectSQL2) do begin
if Aux_SelectSQL2[j] = #13 then
Aux_SelectSQL1 := Aux_SelectSQL1 + ' '
else
if Aux_SelectSQL2[j] <> #10 then
Aux_SelectSQL1 := Aux_SelectSQL1 + Aux_SelectSQL2[j];
end;

//Toma la aparicion del ultimo where
Aux_Pos := 1;
while PosEx(' WHERE ', Aux_SelectSQL1, Aux_Pos) <> 0 do begin
Aux_Pos := PosEx(' WHERE ', Aux_SelectSQL1, Aux_Pos) + 1;
end;

//Divide la sentencia antes del where y despues del where
Dec(Aux_Pos);
if Aux_Pos = 0 then
begin
Aux_Pos := Length(Aux_SelectSQL1);
Aux_SelectSQL1 := Aux_SelectSQL1 + ' WHERE ';
lAgregar := False;
end
else
lAgregar := True;

cInicio := Copy(Aux_SelectSQL1, 1, Aux_Pos + 7);
cFinal := Copy(Aux_SelectSQL1, Aux_Pos + 7, Length(Aux_SelectSQL1));



//Arma el filtro dependiendo la seleccion
if rgTipo.ItemIndex = 0 then //Comienza
cFiltro := ' (' + rgFiltro.Items.Strings[rgFiltro.ItemIndex] + ' like ' + QuotedStr(txBuscar.Text + '%') + ')'
else if rgTipo.ItemIndex = 1 then //Contiene
cFiltro := ' (' + rgFiltro.Items.Strings[rgFiltro.ItemIndex] + ' like ' + QuotedStr('%' + txBuscar.Text + '%') + ')'
else if rgTipo.ItemIndex = 2 then //Termina
cFiltro := ' (' + rgFiltro.Items.Strings[rgFiltro.ItemIndex] + ' like ' + QuotedStr('%' + txBuscar.Text) + ')'
else if rgTipo.ItemIndex = 3 then //Exacta
cFiltro := ' (' + rgFiltro.Items.Strings[rgFiltro.ItemIndex] + ' = ' + QuotedStr(txBuscar.Text) + ')';

//Determina si agrega el AND
if lAgregar then
cFiltro := cFiltro + ' AND ';

sq.SQL.Text := cInicio + cFiltro + cFinal;
sq.Active := True;
sq.EnableControls;