<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python in ArcGIS Enterprise Questions</title>
    <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501834#M39937</link>
    <description>&lt;P&gt;Hi, Tyler! Missed your post originally, but this is definitely something we used to do.&lt;/P&gt;&lt;P&gt;If your service does &lt;STRONG&gt;not &lt;/STRONG&gt;have &lt;EM&gt;Sync&lt;/EM&gt; enabled, you can use &lt;STRONG&gt;FeatureService.manager.truncate() &lt;/STRONG&gt;to empty the table. Assuming your source and destination data types are the same, you can add new data to the layer using &lt;STRONG&gt;FeatureService.edit_features()&lt;/STRONG&gt;, passing &lt;STRONG&gt;dataframe.spatial.to_featureset()&lt;/STRONG&gt; into the &lt;STRONG&gt;adds&lt;/STRONG&gt; parameter.&lt;/P&gt;&lt;P&gt;The API docs suggest not doing this with large numbers of features, so we break up the dataframe into batches.&lt;/P&gt;&lt;P&gt;We wrote up a generic version of this process, which you can find over here:&lt;/P&gt;&lt;P&gt;&lt;A href="https://codeberg.org/kendall-county-gis/project-templates/src/branch/main/generic-etl/truncate-append.py" target="_blank"&gt;https://codeberg.org/kendall-county-gis/project-templates/src/branch/main/generic-etl/truncate-append.py&lt;/A&gt;&lt;/P&gt;&lt;P&gt;There's also an alternative in that file for if you &lt;EM&gt;do &lt;/EM&gt;have sync enabled and Truncate is not an option for you. It's written with a file-based resource in mind, but the source dataframe could come from anywhere.&lt;/P&gt;&lt;P&gt;For the sake of including the code here, here's an even more generalized version:&lt;/P&gt;&lt;LI-CODE lang="python"&gt;from arcgis.gis import GIS
from arcgis.features import GeoAccessor, FeatureLayer

df = GeoAccessor.from_featureclass('some file path') # or replace with something else, like a SQL query or something

fl = FeatureLayer('url')

n = 0
while n &amp;lt; len(df):
    fs = df.iloc[n:n+200].spatial.to_featureset()
    service.edit_features(adds=fs)
    n += 200&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;WARNING: &lt;/STRONG&gt;the ArcGIS Python API and Pandas really don't see eye-to-eye on how to handle datetime values. If you have any datetimes in your source data, be prepared for extra work and frustration to get those added. Your best bet is to convert the dates to their epoch integer, but be sure to use the pandas dtype &lt;STRONG&gt;Int&lt;/STRONG&gt; (note the capital I) so that null values can still be passed through, in the event that your data has empty values in the datetime fields.&lt;/P&gt;&lt;P&gt;I said this was something we &lt;EM&gt;did&lt;/EM&gt;, but we've since moved away from the full truncate/append. If you load the source and destination layers into their own dataframes, you can use pandas.DataFrame.compare(), or even the recently-added gis.features.GeoAccessor.compare() to identify adds, updates, and deletes, and only edit the rows that have changed between the two. For large layers with a low volume of edits, it can really speed up the process.&lt;/P&gt;</description>
    <pubDate>Fri, 05 Jul 2024 14:41:47 GMT</pubDate>
    <dc:creator>jcarlson</dc:creator>
    <dc:date>2024-07-05T14:41:47Z</dc:date>
    <item>
      <title>How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python</title>
      <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1497912#M39870</link>
      <description>&lt;P&gt;Hello,&lt;BR /&gt;&lt;BR /&gt;I use python extensively to ETL data and write dataframes to our ArcGIS Enterprise MS SQL Server using GeoAccessor method to_featureclass.&amp;nbsp; Given the limitations of table handling for&amp;nbsp;GeoAccessor method to_featureclass mentioned here...&lt;BR /&gt;&lt;BR /&gt;&lt;A href="https://community.esri.com/t5/arcgis-api-for-python-ideas/additional-table-handling-for-geoaccessor-method/idi-p/1207381" target="_blank" rel="noopener"&gt;https://community.esri.com/t5/arcgis-api-for-python-ideas/additional-table-handling-for-geoaccessor-method/idi-p/1207381&lt;/A&gt;&lt;/P&gt;&lt;P&gt;...what are folks doing to truncate then insert into tables.&amp;nbsp; In other words, rather than dropping a table and creating a new table how does one delete all records (keeping schema/columns) and insert new rows.&lt;BR /&gt;&lt;BR /&gt;Using the to_feature class as-is (dropping tables) constantly runs into locked schema tables, on top of probably not being a best practice.&lt;BR /&gt;&lt;BR /&gt;Please chime in with your work flow...good, bad, or ugly.&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;Thank you,&lt;BR /&gt;&lt;BR /&gt;Tyler&lt;/P&gt;</description>
      <pubDate>Thu, 27 Jun 2024 13:25:02 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1497912#M39870</guid>
      <dc:creator>TylerT</dc:creator>
      <dc:date>2024-06-27T13:25:02Z</dc:date>
    </item>
    <item>
      <title>Re: How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python</title>
      <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501834#M39937</link>
      <description>&lt;P&gt;Hi, Tyler! Missed your post originally, but this is definitely something we used to do.&lt;/P&gt;&lt;P&gt;If your service does &lt;STRONG&gt;not &lt;/STRONG&gt;have &lt;EM&gt;Sync&lt;/EM&gt; enabled, you can use &lt;STRONG&gt;FeatureService.manager.truncate() &lt;/STRONG&gt;to empty the table. Assuming your source and destination data types are the same, you can add new data to the layer using &lt;STRONG&gt;FeatureService.edit_features()&lt;/STRONG&gt;, passing &lt;STRONG&gt;dataframe.spatial.to_featureset()&lt;/STRONG&gt; into the &lt;STRONG&gt;adds&lt;/STRONG&gt; parameter.&lt;/P&gt;&lt;P&gt;The API docs suggest not doing this with large numbers of features, so we break up the dataframe into batches.&lt;/P&gt;&lt;P&gt;We wrote up a generic version of this process, which you can find over here:&lt;/P&gt;&lt;P&gt;&lt;A href="https://codeberg.org/kendall-county-gis/project-templates/src/branch/main/generic-etl/truncate-append.py" target="_blank"&gt;https://codeberg.org/kendall-county-gis/project-templates/src/branch/main/generic-etl/truncate-append.py&lt;/A&gt;&lt;/P&gt;&lt;P&gt;There's also an alternative in that file for if you &lt;EM&gt;do &lt;/EM&gt;have sync enabled and Truncate is not an option for you. It's written with a file-based resource in mind, but the source dataframe could come from anywhere.&lt;/P&gt;&lt;P&gt;For the sake of including the code here, here's an even more generalized version:&lt;/P&gt;&lt;LI-CODE lang="python"&gt;from arcgis.gis import GIS
from arcgis.features import GeoAccessor, FeatureLayer

df = GeoAccessor.from_featureclass('some file path') # or replace with something else, like a SQL query or something

fl = FeatureLayer('url')

n = 0
while n &amp;lt; len(df):
    fs = df.iloc[n:n+200].spatial.to_featureset()
    service.edit_features(adds=fs)
    n += 200&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;WARNING: &lt;/STRONG&gt;the ArcGIS Python API and Pandas really don't see eye-to-eye on how to handle datetime values. If you have any datetimes in your source data, be prepared for extra work and frustration to get those added. Your best bet is to convert the dates to their epoch integer, but be sure to use the pandas dtype &lt;STRONG&gt;Int&lt;/STRONG&gt; (note the capital I) so that null values can still be passed through, in the event that your data has empty values in the datetime fields.&lt;/P&gt;&lt;P&gt;I said this was something we &lt;EM&gt;did&lt;/EM&gt;, but we've since moved away from the full truncate/append. If you load the source and destination layers into their own dataframes, you can use pandas.DataFrame.compare(), or even the recently-added gis.features.GeoAccessor.compare() to identify adds, updates, and deletes, and only edit the rows that have changed between the two. For large layers with a low volume of edits, it can really speed up the process.&lt;/P&gt;</description>
      <pubDate>Fri, 05 Jul 2024 14:41:47 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501834#M39937</guid>
      <dc:creator>jcarlson</dc:creator>
      <dc:date>2024-07-05T14:41:47Z</dc:date>
    </item>
    <item>
      <title>Re: How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python</title>
      <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501935#M39938</link>
      <description>&lt;P&gt;&lt;a href="https://community.esri.com/t5/user/viewprofilepage/user-id/363906"&gt;@jcarlson&lt;/a&gt;,&lt;BR /&gt;Thanks for your reply.&amp;nbsp; I had a major typo in my initial post subject, so you might not have missed it, rather skipped it due to the typo title.&amp;nbsp; Anyway, I'll study your response and see if I can work it into our workflow.&lt;BR /&gt;&lt;BR /&gt;It's too bad we didn't have something like this with GeoAccessor:&lt;/P&gt;&lt;LI-CODE lang="python"&gt;to_featureclass(location, how=append, has_z=None, has_m=None, sanitize_columns=True)

Parameter: how
Description: Optional String. Options include
drop_create_insert, truncate_append. The
default it append. &lt;/LI-CODE&gt;&lt;P&gt;&lt;BR /&gt;The location is an sde connection to an EGDB giving direct access to EGDB via sde connection instead of through the portal via GIS().&amp;nbsp; I don't have any real reason to want to avoid GIS(), other than why not cut out the middle man and write directly to EDGB?&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;Tyler&lt;BR /&gt;Enterprise 11.2&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;</description>
      <pubDate>Fri, 05 Jul 2024 17:14:11 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501935#M39938</guid>
      <dc:creator>TylerT</dc:creator>
      <dc:date>2024-07-05T17:14:11Z</dc:date>
    </item>
    <item>
      <title>Re: How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python</title>
      <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501938#M39939</link>
      <description>&lt;P&gt;If the layer isn't using any kind of versioning, writing straight to the EGDB is doable. You don't need GIS at all, just pandas. I've never done it with &lt;EM&gt;geometry&lt;/EM&gt; though, and we aren't using MSSQL, so it may be trickier in actual practice than in theory.&lt;/P&gt;&lt;P&gt;&lt;A href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html" target="_blank"&gt;https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Fri, 05 Jul 2024 17:20:47 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501938#M39939</guid>
      <dc:creator>jcarlson</dc:creator>
      <dc:date>2024-07-05T17:20:47Z</dc:date>
    </item>
    <item>
      <title>Re: How best to write dataframes to ArcGIS Enterprise MS SQL Server using Python</title>
      <link>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501966#M39940</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.esri.com/t5/user/viewprofilepage/user-id/363906"&gt;@jcarlson&lt;/a&gt;&amp;nbsp;et al,&lt;BR /&gt;&lt;BR /&gt;Following my initial post awaiting responses I developed the following non-spatial tables.&amp;nbsp; I prefer using SqlAlchemy but other engines will do.&lt;/P&gt;&lt;LI-CODE lang="python"&gt;def df_write(df, tbl, engine):
    dtype_sa = &amp;lt;your data types dict&amp;gt;
    with engine.connect() as con:
        df.to_sql(name=tbl, con=con, if_exists='append', index=False, dtype=dtype_sa)
        print("*** SQL WRITTEN ***")
        
def truncate(tbl_name, pk, pk_dtype, engine):
    table = map_table_wo_pk(tbl_name, pk, pk_dtype, engine) #orm automapper func
    with orm.Session(engine) as session:
        session.query(table).delete()
        session.commit()

def df_trunc_insert(df, tbl, pk, pk_dtype, sde, engine):
    if tbl_exists(tbl):  
        print(f'Table "{tbl}" exists.  Truncating and inserting table.')
        truncate(tbl, pk, pk_dtype, engine)
        df['OBJECTID'] = np.arange(df.shape[0]) + 1 # OBJECTID must not be null
        df['GlobalID'] = [uuid.uuid4() for _ in np.arange(df.shape[0])] # GUID must not be null
        df_write(df, tbl, engine) 
    else:
        print(f'Table "{tbl}" does not exist.  Creating new table.')
        df.spatial.to_table(location=sde/tbl) #This method uppers() the table title and adds OBJECTID, also upper()
        arcpy.env.workspace = str(sde)
        print(f'Adding GUID/UUID')
        arcpy.management.AddGlobalIDs(tbl)

if __name__ == "__main__":
    df_trunc_insert(&amp;lt;df&amp;gt;, &amp;lt;tbl&amp;gt;, &amp;lt;pk&amp;gt;, &amp;lt;pk_dtype&amp;gt;, &amp;lt;sde (pathlib)&amp;gt;, &amp;lt;your engine&amp;gt;)&lt;/LI-CODE&gt;&lt;P data-unlink="true"&gt;&lt;BR /&gt;The idea here is upon initial table creation to use geoaccessor&amp;nbsp;df.spatial.to_table().&amp;nbsp; df.spatial.to_table() checks data typing, registers the table with the EGDB, and maybe a few other things behind the curtains.&amp;nbsp; Then, since&amp;nbsp;df.spatial.to_table() doesn't have a truncate, revert to SqlAlchemy to truncate (hold schema) and insert for subsequent overwrites (if table exists).&amp;nbsp; A few extra steps are required to repopulate ObjectID and repopulate GUID.&amp;nbsp; And finally, take care of a little gotchya fact that&amp;nbsp;df.spatial.to_table() doesn't set PKs, but SqlAlchemy automap reflection requires PKs...surmountable with proper automap setup.&amp;nbsp;&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;I haven't setup a script for spatial tables yet, but do foresee issues with the geometry column and SqlAlchemy passing it to the EDGB properly.&lt;BR /&gt;&lt;BR /&gt;I hoping to get as many ideas for best practices here.&amp;nbsp; I have many production use cases, as I'm sure others do, for all of our automated dataframes to update our EGDB tables &lt;STRONG&gt;without&lt;/STRONG&gt; dropping the EGDB table.&lt;BR /&gt;&lt;BR /&gt;Tyler&lt;BR /&gt;Enterprise 11.2&lt;/P&gt;</description>
      <pubDate>Fri, 05 Jul 2024 18:08:50 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-enterprise-questions/how-best-to-write-dataframes-to-arcgis-enterprise/m-p/1501966#M39940</guid>
      <dc:creator>TylerT</dc:creator>
      <dc:date>2024-07-05T18:08:50Z</dc:date>
    </item>
  </channel>
</rss>

