簡體   English   中英

具有附加值的Python MySQL Connector executemany

[英]Python MySQL Connector executemany with Extra Values

使用MySQL的executemany()插入數據時,有沒有內置的方法可以忽略字典中的字段?

我需要從JSON文件中提供的相對較大的數據集中插入數據。 因此,JSON數據的基本布局為:

{
    "data" : [
        { "f1" : 42, "f2" : "abc", "f99" : "useless stuff" },
        { "f1" : 43, "f2" : "def", "f99" : [ "junk", "here" ] },
        { "f1" : 44, "f2" : "ghi", "f99" : { "thing" : 99 } }
    ]
}

我有一個看起來像這樣的插入設置:

import json
import mysql.connector
with open( 'huge_data_dump.json', 'rb' ) as fh:
    data = json.load( fh )
connection = mysql.connector.connect( **mysql_config )
cursor = connection.cursor()
query = 'INSERT INTO `example` ( `f1`, `f2` ) VALUES ( %(f1)s, %(f2)s )'
cursor.executemany( query, data[ 'data' ] )
cursor.close()
connection.close()

目標表如下所示:

CREATE TABLE `example` ( `f1` INT, `f2` VARCHAR( 10 ) )

但是,當我運行此命令時,出現錯誤:

Failed processing pyformat-parameters; Python 'list' cannot be converted to a MySQL type

如果我僅將導入限制為示例數據集中的第一行,則插入效果很好:

cursor.executemany( query, data[ 'data' ][ : 1 ] )

問題來自f99字段中包含誰知道什么的多余數據。 這對很好:我不需要f99任何信息。 但是,MySQL連接器似乎希望在檢查查詢以查看是否需要該值之前將整個記錄的字典轉換為安全字符串。

我嘗試使用生成器函數將數據集過濾到對executemany()的調用中,但是連接器抱怨只能接受元組和列表(我覺得這是一個非Pythonic的接口)。

我的最后一executemany()是將數據復制到新字典中,並在將數據傳遞給executemany()之前過濾掉不需要的字段。 但是,這些數據集已經足夠大了,在這里我考慮從JSON源文件中以一次幾百次插入的組流式傳輸它們。 試圖消除所有不需要的數據的附加循環將是浪費,需要維護更多代碼。 我衷心希望我能忽略文檔中沒有涵蓋或掩蓋的內容。

我想我可以開始研究輸入上的一些自定義JSON過濾,但是,我再次希望有一種簡單的內置方法來解決(似乎是)一個相對常見的用例。

您可以使用生成器為數據列表中的每個記錄創建所需列的元組:

(d["f1"], d["f2"] for d in data['data'])

將此生成器傳遞給executemany函數應該可以正常工作。

編輯:您可能需要將查詢更改為

query = 'INSERT INTO `example` ( `f1`, `f2` ) VALUES ( %s, %s )'

但是我不太確定。

親愛的未來人們:

經過一段時間的解決后,我決定從輸入端解決問題。

內置的JSON實現不完全支持流傳輸,但是您可以為JSON數據的各個部分指定自定義解碼,因為它們已加載到解釋器的內存中。 我利用能夠攔截所有對象到字典的解碼的能力,決定繼續操作那里的傳入數據。

還要注意:MySQL連接器對在一次事務中傳遞的數據量有一些限制,因此我繼續在解碼器中緩存了幾百個這些“轉換后的”字典,並將它們作為文件插入數據庫中。由JSON load()函數讀取。

簡而言之:

import json

class CustomDecoder( json.JSONDecoder ):

    allowed = [ 'f1', 'f1' ]

    def __init__( self, **kwargs ):
        kwargs[ 'object_hook' ] = self.object_to_dict
        super( CustomDecoder, self ).__init__( **kwargs )
        self.cache = []

    def object_to_dict( self, data ):

        # this check just identifies the object we need to insert
        if 'f1' in data:

            # permit allowed fields from the incoming dictionary
            data = dict(
                ( k, v )
                for k, v in data.iteritems()
                if k in self.allowed
            )

            # add data to batch cache
            self.cache.append( data )

            # check cache status
            if len( self.cache ) >= 200:

                # insert the cached records as a group for performance
                ### cursor.executemany( query, self.cache )

                # dump the cache
                self.cache = []

        # return modified data or pass through un-modified data
        return data

# connect to database, grab a cursor to it, set up the query

with open( 'import_file.json', 'rb' ) as fh:
    data = json.load( fh, cls = CustomDecoder )

# at this point, everything but left-overs in the cache should be inserted

注意事項:

  1. 解析器完成數據加載后,您仍然需要插入緩存中的所有剩余數據。 我最終將緩存保留在CustomDecoder實例之外,因此一旦內部創建的CustomDecoder消失,便可以刷新它。
  2. 管理查詢和游標對象需要更多代碼才能保持界面相對干凈。 我決定創建一個分配給class屬性的回調處理程序。 回調處理程序碰巧知道如何找到當前的游標和查詢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM