En \/blogs\/tilting\/2016\/09\/27\/performance-at-a-price-file-geodatabases-and-sql-support?sr=search&searchId=c3a45d0d-24f6-481b-bcf7-cfc7493b25fc&searchIndex=0<\/A>, llamo la atención sobre la compensación en el soporte SQL que Esri hizo al desarrollar la file geodatabase (FGDB) como reemplazo de la personal geodatabase (PGDB). En esa publicación, se proporcionan varios enlaces para quienes estén interesados en aprender más sobre el soporte SQL en file geodatabases. Aunque hay bastante superposición en el contenido entre las diversas fuentes\/enlaces, también hay declaraciones importantes que solo existen en un lugar u otro, y conocer eso puede ser importante al solucionar errores o tratar de entender resultados espurios de datos almacenados en file geodatabases.<\/P><\/P>Un área donde los usuarios pueden meterse en problemas con file geodatabases y SQL es la condición u operador EXISTS. Al mirar la Referencia SQL (FileGDB_SQL.htm) en la File Geodatabase API ( @Esri Downloads <\/A>) o la Referencia SQL para expresiones de consulta usadas en ArcGIS<\/A>:<\/P>[NOT] EXISTS<\/P><\/TD>Devuelve TRUE si la subconsulta devuelve al menos un registro, de lo contrario, devuelve FALSE. Por ejemplo, esta expresión devuelve TRUE si el campo OBJECTID contiene un valor de 50:<\/P>EXISTS (SELECT * FROM parcels WHERE "OBJECTID" = 50)<\/PRE>EXISTS es compatible solo con file, personal y ArcSDE geodatabases.<\/TD><\/TR><\/TBODY><\/TABLE><\/BLOCKQUOTE><\/P>Viendo que SQL EXISTS es compatible con la file geodatabase, ¿cómo alguien puede meterse en problemas usándolo? Desafortunadamente, es más fácil de lo que podría esperar, y la respuesta son las subconsultas correlacionadas.<\/P><\/P>Las subconsultas correlacionadas son bastante comunes cuando se trabaja con EXISTS, tan comunes de hecho que la documentación de Microsoft para EXISTS (Transact-SQL)<\/A> y la documentación de Oracle para Condición EXISTS <\/A>ambas usan una subconsulta correlacionada en mínimo un ejemplo de código. En su forma más simple, una subconsulta correlacionada es una subconsulta que se relaciona con una o más tablas en la consulta externa. La teoría y empirismo de las subconsultas correlacionadas va mucho más allá de esta publicación del blog, pero mencionaré que las subconsultas correlacionadas tienen algunas propiedades similares a JOIN, o al menos apariencias.<\/P><\/P>El siguiente ejemplo está adaptado de Select Max value arcpy<\/A>, la misma pregunta en GeoNet que me llevó a investigar este problema hace muchos meses. Comencemos con dos tablas básicas (TableA y TableB), cada una con un campo texto y un campo entero, y aunque una tabla contiene un subconjunto de registros de la otra tabla.<\/P><\/P><\/P>>><\/SPAN>><\/SPAN> fgdb =<\/SPAN> # ruta a file geodatabase<\/SPAN>\n>><\/SPAN>><\/SPAN> pgdb =<\/SPAN> # ruta a personal geodatabase<\/SPAN>\n>><\/SPAN>><\/SPAN> egdb =<\/SPAN> # ruta a enterprise geodatabase, SQL Server usado en el ejemplo<\/SPAN>\n>><\/SPAN>><\/SPAN> gpkg =<\/SPAN> # ruta a GeoPackage<\/SPAN>\n>><\/SPAN>><\/SPAN> gdbs (<\/)fgdb<SPAN class="punctuation token">,<\/) pgdb<SPAN class="punctuation token">,<\/) egdb<SPAN class="punctuation token">,<\/) gpkg<SPANCspanclass=punctuationtoken>)>>>>>> values = (("A", 1), ("A", 2), ("B", 1),
<\/P>
Un área donde los usuarios pueden meterse en problemas con file geodatabases y SQL es la condición u operador EXISTS. Al mirar la Referencia SQL (FileGDB_SQL.htm) en la File Geodatabase API (
[NOT] EXISTS<\/P><\/TD>Devuelve TRUE si la subconsulta devuelve al menos un registro, de lo contrario, devuelve FALSE. Por ejemplo, esta expresión devuelve TRUE si el campo OBJECTID contiene un valor de 50:<\/P>EXISTS (SELECT * FROM parcels WHERE "OBJECTID" = 50)<\/PRE>EXISTS es compatible solo con file, personal y ArcSDE geodatabases.<\/TD><\/TR><\/TBODY><\/TABLE><\/BLOCKQUOTE><\/P>Viendo que SQL EXISTS es compatible con la file geodatabase, ¿cómo alguien puede meterse en problemas usándolo? Desafortunadamente, es más fácil de lo que podría esperar, y la respuesta son las subconsultas correlacionadas.<\/P><\/P>Las subconsultas correlacionadas son bastante comunes cuando se trabaja con EXISTS, tan comunes de hecho que la documentación de Microsoft para
[NOT] EXISTS<\/P><\/TD>
Devuelve TRUE si la subconsulta devuelve al menos un registro, de lo contrario, devuelve FALSE. Por ejemplo, esta expresión devuelve TRUE si el campo OBJECTID contiene un valor de 50:<\/P>
EXISTS (SELECT * FROM parcels WHERE "OBJECTID" = 50)<\/PRE>EXISTS es compatible solo con file, personal y ArcSDE geodatabases.<\/TD><\/TR><\/TBODY><\/TABLE><\/BLOCKQUOTE><\/P>Viendo que SQL EXISTS es compatible con la file geodatabase, ¿cómo alguien puede meterse en problemas usándolo? Desafortunadamente, es más fácil de lo que podría esperar, y la respuesta son las subconsultas correlacionadas.<\/P><\/P>Las subconsultas correlacionadas son bastante comunes cuando se trabaja con EXISTS, tan comunes de hecho que la documentación de Microsoft para
El siguiente ejemplo está adaptado de
>><\/SPAN>><\/SPAN> fgdb =<\/SPAN> # ruta a file geodatabase<\/SPAN>\n>><\/SPAN>><\/SPAN> pgdb =<\/SPAN> # ruta a personal geodatabase<\/SPAN>\n>><\/SPAN>><\/SPAN> egdb =<\/SPAN> # ruta a enterprise geodatabase, SQL Server usado en el ejemplo<\/SPAN>\n>><\/SPAN>><\/SPAN> gpkg =<\/SPAN> # ruta a GeoPackage<\/SPAN>\n>><\/SPAN>><\/SPAN> gdbs (<\/)fgdb<SPAN class="punctuation token">,<\/) pgdb<SPAN class="punctuation token">,<\/) egdb<SPAN class="punctuation token">,<\/) gpkg<SPANCspanclass=punctuationtoken>)>>>>>> values = (("A", 1), ("A", 2), ("B", 1),
File Geodatabase does not support correlated subqueries,
After publishing the blog post, I got to wondering how GeoPackages are handled. It turns out, they work just fine, i.e., one can create the tables and table view from a GeoPackage and get the correct results. I went ahead and updated the code to include using a GeoPackage.
lshipman-esristaff, thanks for the inside scoop. Do you have any idea if that recognition of response differences in geodatabases means that the documentation will be updated to be a bit more explicit, that there will be fixes (next version, patches, hotfixes) to allow SQL Correlated Subqueries to work in fGDB like they work in other data formats in the future, or is it just that this as far as it goes for the time? Thanks in advance.
I plan on updating the documentation Post 10.5. Support of SQL Correlated Subqueries is not currently planned for the fGDB, but if a persuasive business case is submitted we will consider it.
Although my blog post example involved two tables, one of our most common uses for correlated subqueries is actually self-correlated subqueries:
<SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> fgdb <SPAN class="operator token">=</SPAN> <SPAN class="comment token"># path to file geodatabase</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> pgdb <SPAN class="operator token">=</SPAN> <SPAN class="comment token"># path to personal geodatabase</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> egdb <SPAN class="operator token">=</SPAN> <SPAN class="comment token"># path to enterprise geodatabase, SQL Server used in example</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> gpkg <SPAN class="operator token">=</SPAN> <SPAN class="comment token"># path to GeoPackage</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> gdbs <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">(</SPAN>fgdb<SPAN class="punctuation token">,</SPAN> pgdb<SPAN class="punctuation token">,</SPAN> egdb<SPAN class="punctuation token">,</SPAN> gpkg<SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> values <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"A"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"A"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"B"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"B"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"C"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"C"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">3</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"D"</SPAN><SPAN class="punctuation token">,</SPAN> None<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"E"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> table_name <SPAN class="operator token">=</SPAN> <SPAN class="string token">"TableA"</SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> <SPAN class="operator token">>></SPAN><SPAN class="operator token">></SPAN> <SPAN class="keyword token">for</SPAN> gdb <SPAN class="keyword token">in</SPAN> gdbs<SPAN class="punctuation token">:</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> table <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>CreateTable_management<SPAN class="punctuation token">(</SPAN>gdb<SPAN class="punctuation token">,</SPAN> table_name<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>AddField_management<SPAN class="punctuation token">(</SPAN>table<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"id"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"TEXT"</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>AddField_management<SPAN class="punctuation token">(</SPAN>table<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"version"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"LONG"</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="keyword token">with</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>da<SPAN class="punctuation token">.</SPAN>InsertCursor<SPAN class="punctuation token">(</SPAN>table<SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"id"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"version"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">as</SPAN> cur<SPAN class="punctuation token">:</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="keyword token">for</SPAN> value <SPAN class="keyword token">in</SPAN> values<SPAN class="punctuation token">:</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> cur<SPAN class="punctuation token">.</SPAN>insertRow<SPAN class="punctuation token">(</SPAN>value<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> qry <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="string token">"NOT EXISTS (SELECT 1"</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="string token">" FROM TableA a"</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="string token">" WHERE TableA.id = a.id"</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="string token">" AND TableA.version < a.version)"</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> table_view <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>MakeTableView_management<SPAN class="punctuation token">(</SPAN>table<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"table_view"</SPAN><SPAN class="punctuation token">,</SPAN> qry<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>TableToTable_conversion<SPAN class="punctuation token">(</SPAN>table_view<SPAN class="punctuation token">,</SPAN> gdb<SPAN class="punctuation token">,</SPAN> table_name <SPAN class="operator token">+</SPAN> <SPAN class="string token">"_max"</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="keyword token">print</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>GetCount_management<SPAN class="punctuation token">(</SPAN>table_view<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>Delete_management<SPAN class="punctuation token">(</SPAN>table_view<SPAN class="punctuation token">)</SPAN> <SPAN class="number token">8</SPAN> <SPAN class="number token">5</SPAN> <SPAN class="number token">5</SPAN> <SPAN class="number token">5</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
The use of a self-correlated subquery allows for selecting the maximum or minimum record for a given attribute. In the example above, a self-correlated subquery is used to select the record with the maximum value of version for each value of id.
@LanceShipman
Regarding:
"File Geodatabase does not support correlated subqueries"
Does that still apply to ArcGIS Pro 3.0.3? There seems to be a lot of stuff supported in FGDBs these days, including INNER JOIN (SQL for reporting and analysis on file geodatabases) and database views. I'd like to think correlated subqueries would be supported in this modern age.
I ask because of a related post I wrote here: File Geodatabase SQL expression to get greatest n per group
Related ideas:
BUG-000156143 - An SQL query containing the EXISTS predicate validates successfully but returns incorrect results from a file geodatabase.ENH-000156158 - Raise exception when using unsupported SQL expressions in file geodatabases.
Esri Canada Case 03264937 - Exception not raised when unsupported FGDB SQL used (correlated subquery)
10-25-2016 12:35 PMI plan on updating the documentation Post 10.5. Support of SQL Correlated Subqueries is not currently planned for the fGDB, but if a persuasive business case is submitted we will consider it.
10-25-2016 12:35 PM
I would say the "greatest 1 per group" scenario is a worthwhile business case for supporting correlated subqueries in file geodatabases. Currently, there doesn't seem to be a way to do that in FGDB SQL expressions.
It's a common requirement. For example: Selecting the most recent records based on unique values in another field
Los miembros registrados pueden publicar, seguir actualizaciones y más. ¿Nuevo aquí? Regístrate gratis.
Find useful guides, FAQs, and documents to help you navigate and make the most of Esri Community.