【问题标题】:Generate SQL statements from a Pandas Dataframe从 Pandas 数据框生成 SQL 语句
【发布时间】:2015-09-13 08:02:43
【问题描述】:

我正在将来自各种来源(csv、xls、json 等)的数据加载到 Pandas 数据帧中,我想生成语句来创建并用这些数据填充 SQL 数据库。有谁知道这样做的方法吗?

我知道 pandas 有一个to_sql 函数,但它只适用于数据库连接,它不能生成字符串。

示例

我想要的是这样的数据框:

import pandas as pd
import numpy as np

dates = pd.date_range('20130101',periods=6)
df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=list('ABCD'))

还有一个会生成这个的函数(这个例子是 PostgreSQL,但任何一个都可以):

CREATE TABLE data
(
  index timestamp with time zone,
  "A" double precision,
  "B" double precision,
  "C" double precision,
  "D" double precision
)

【问题讨论】:

    标签: python sql pandas


    【解决方案1】:

    如果你只想要'CREATE TABLE' sql 代码(而不是插入数据),你可以使用 pandas.io.sql 模块的get_schema 函数:

    In [10]: print pd.io.sql.get_schema(df.reset_index(), 'data')
    CREATE TABLE "data" (
      "index" TIMESTAMP,
      "A" REAL,
      "B" REAL,
      "C" REAL,
      "D" REAL
    )
    

    一些注意事项:

    • 我不得不使用reset_index,否则它不包含索引
    • 如果您提供某种数据库风格的 sqlalchemy 引擎,则结果将调整为该风格(例如数据类型名称)。

    【讨论】:

    • 是否可以获取插入数据,以便进行更新/插入?
    • @joris 如何指定 sqlalchemy 引擎?
    • 我看到 RustyShackleford 已经找到了它,但这里是您指定引擎的方式:stackoverflow.com/a/51294670/3279262
    【解决方案2】:

    从数据帧生成 SQL 创建语句

    SOURCE = df
    TARGET = data
    

    从数据帧生成 SQL 创建语句

    def SQL_CREATE_STATEMENT_FROM_DATAFRAME(SOURCE, TARGET):
    
    # SQL_CREATE_STATEMENT_FROM_DATAFRAME(SOURCE, TARGET)
    # SOURCE: source dataframe
    # TARGET: target table to be created in database
    
        import pandas as pd
        sql_text = pd.io.sql.get_schema(SOURCE.reset_index(), TARGET)   
        return sql_text
    

    检查 SQL CREATE TABLE 语句字符串

    print('\n\n'.join(sql_text))
    

    从数据帧生成 SQL 插入语句

    def SQL_INSERT_STATEMENT_FROM_DATAFRAME(SOURCE, TARGET):
        sql_texts = []
        for index, row in SOURCE.iterrows():       
            sql_texts.append('INSERT INTO '+TARGET+' ('+ str(', '.join(SOURCE.columns))+ ') VALUES '+ str(tuple(row.values)))        
        return sql_texts
    

    检查 SQL INSERT INTO 语句字符串

    print('\n\n'.join(sql_texts))
    

    【讨论】:

    • 工作得很好。基于此解决方案创建更新语句的最佳方法是什么?
    • 我试过这个插入语句,但它似乎不能处理 Nulls 。生成的插入语句有 nan 用于 NULL 值,我的查询试图在这些字段中插入 nan。
    【解决方案3】:

    插入语句解决方案

    不确定这是否是绝对最好的方法,但这比使用df.iterrows() 更有效,因为这非常慢。这还借助正则表达式处理 nan 值。

    def get_insert_query_from_df(df, dest_table):
    
        insert = """
        INSERT INTO `{dest_table}` (
            """.format(dest_table=dest_table)
    
        columns_string = str(list(df.columns))[1:-1]
        columns_string = re.sub(r' ', '\n        ', columns_string)
        columns_string = re.sub(r'\'', '', columns_string)
    
        values_string = ''
    
        for row in df.itertuples(index=False,name=None):
            values_string += re.sub(r'nan', 'null', str(row))
            values_string += ',\n'
    
        return insert + columns_string + ')\n     VALUES\n' + values_string[:-2] + ';'
    

    【讨论】:

      【解决方案4】:

      如果您想自己编写文件,您还可以检索列名和 dtypes 并构建字典以将 pandas 数据类型转换为 sql 数据类型。

      举个例子:

      import pandas as pd
      import numpy as np
      
      dates = pd.date_range('20130101',periods=6)
      df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=list('ABCD'))
      
      tableName = 'table'
      columnNames = df.columns.values.tolist()
      columnTypes =  map(lambda x: x.name, df.dtypes.values)
      
      # Storing column names and dtypes in a dataframe
      
      tableDef = pd.DataFrame(index = range(len(df.columns) + 1), columns=['cols', 'dtypes'])
      
      tableDef.iloc[0]           = ['index', df.index.dtype.name]
      tableDef.loc[1:, 'cols']   = columnNames
      tableDef.loc[1:, 'dtypes'] = columnTypes
      
      # Defining a dictionnary to convert dtypes
      
      conversion = {'datetime64[ns]':'timestamp with time zone', 'float64':'double precision'}
      
      # Writing sql in a file
      
      f = open('yourdir\%s.sql' % tableName, 'w')
      
      f.write('CREATE TABLE %s\n' % tableName)
      f.write('(\n')
      
      for i, row in tableDef.iterrows():
          sep = ",\n" if i < tableDef.index[-1] else "\n"
          f.write('\t\"%s\" %s%s' % (row['cols'], conversion[row['dtypes']], sep))
      
      f.write(')')
      
      f.close()
      

      您可以使用相同的方式使用 INSERT INTO 填充表格。

      【讨论】:

        【解决方案5】:

        单插入查询解决方案

        我没有找到满足我需要的上述答案。我想为数据框创建 一个 单个插入语句,每行作为值。这可以通过以下方式实现:

        import re 
        import pandas as pd 
        
        table = 'your_table_name_here'
        
        # You can read from CSV file here... just using read_sql_query as an example
        
        df = pd.read_sql_query(f'select * from {table}', con=db_connection)
        
        
        cols = ', '.join(df.columns.to_list()) 
        vals = []
        
        for index, r in df.iterrows():
            row = []
            for x in r:
                row.append(f"'{str(x)}'")
        
            row_str = ', '.join(row)
            vals.append(row_str)
        
        f_values = [] 
        for v in vals:
            f_values.append(f'({v})')
        
        # Handle inputting NULL values
        f_values = ', '.join(f_values) 
        f_values = re.sub(r"('None')", "NULL", f_values)
        
        sql = f"insert into {table} ({cols}) values {f_values};" 
        
        print(sql)
        
        db.dispose()
        

        【讨论】:

          【解决方案6】:

          以用户@Jaris 的帖子获取 CREATE,我进一步扩展它以适用于任何 CSV

          import sqlite3
          import pandas as pd
          
          db = './database.db'
          csv = './data.csv'
          table_name = 'data'
          
          # create db and setup schema
          df = pd.read_csv(csv)
          create_table_sql = pd.io.sql.get_schema(df.reset_index(), table_name)
          conn = sqlite3.connect(db)
          c = conn.cursor()
          c.execute(create_table_sql)
          conn.commit()
          
          
          # now we can insert data
          def insert_data(row, c):
              values = str(row.name)+','+','.join([str('"'+str(v)+'"') for v in row])
              sql_insert=f"INSERT INTO {table_name} VALUES ({values})"
          
              try:
                  c.execute(sql_insert)
              except Exception as e:
                  print(f"SQL:{sql_insert} \n failed with Error:{e}")
          
          
          
          # use apply to loop over dataframe and call insert_data on each row
          df.apply(lambda row: insert_data(row, c), axis=1)
          
          # finally commit all those inserts into the database
          conn.commit()
          

          希望这比其他答案更简单,更pythonic!

          【讨论】:

            【解决方案7】:

            取决于您是否可以放弃生成 SQL 语句的中间表示;您也可以直接执行插入语句。

            con.executemany("INSERT OR REPLACE INTO data (A, B, C, D) VALUES (?, ?, ?, ?, ?)", list(df_.values))
            

            这工作得更好一些,因为字符串生成的麻烦更少。

            【讨论】:

              【解决方案8】:

              如果您只是想根据pandas.DataFrame 生成带有插入的字符串 - 我建议您使用 @rup 建议的批量 sql 插入语法。

              以下是我为此目的编写的function 示例:

              import pandas as pd
              import re
              
              
              def df_to_sql_bulk_insert(df: pd.DataFrame, table: str, **kwargs) -> str:
                  """Converts DataFrame to bulk INSERT sql query
                  >>> data = [(1, "_suffixnan", 1), (2, "Noneprefix", 0), (3, "fooNULLbar", 1, 2.34)]
                  >>> df = pd.DataFrame(data, columns=["id", "name", "is_deleted", "balance"])
                  >>> df
                     id        name  is_deleted  balance
                  0   1  _suffixnan           1      NaN
                  1   2  Noneprefix           0      NaN
                  2   3  fooNULLbar           1     2.34
                  >>> query = df_to_sql_bulk_insert(df, "users", status="APPROVED", address=None)
                  >>> print(query)
                  INSERT INTO users (id, name, is_deleted, balance, status, address)
                  VALUES (1, '_suffixnan', 1, NULL, 'APPROVED', NULL),
                         (2, 'Noneprefix', 0, NULL, 'APPROVED', NULL),
                         (3, 'fooNULLbar', 1, 2.34, 'APPROVED', NULL);
                  """
                  df = df.copy().assign(**kwargs)
                  columns = ", ".join(df.columns)
                  tuples = map(str, df.itertuples(index=False, name=None))
                  values = re.sub(r"(?<=\W)(nan|None)(?=\W)", "NULL", (",\n" + " " * 7).join(tuples))
                  return f"INSERT INTO {table} ({columns})\nVALUES {values};"
              

              顺便说一句,它将nan/None 条目转换为NULL,并且可以将常量列=值对作为关键字参数传递(请参阅文档字符串示例中的status="APPROVED"address=None 参数)。

              一般来说,它的工作速度更快,因为任何数据库都为单个插入执行大量工作:检查约束、构建索引、刷新、写入日志等。数据库可以在执行多次插入时优化这种复杂的操作-one 操作,而不是一个一个地调用引擎。

              【讨论】:

                猜你喜欢
                • 2021-08-22
                • 2014-09-11
                • 1970-01-01
                • 1970-01-01
                • 2023-03-15
                • 2021-06-14
                • 1970-01-01
                • 2011-12-05
                • 1970-01-01
                相关资源
                最近更新 更多