यहाँ Python 3 के लिए COPY FROM का बाइनरी समकक्ष है:
from io import BytesIO
from struct import pack
import psycopg2
# Two rows of data; "id" is not in the upstream data source
# Columns: node, ts, val1, val2
data = [(23253, 342, -15.336734, 2494627.949375),
(23256, 348, 43.23524, 2494827.949375)]
conn = psycopg2.connect("dbname=mydb user=postgres")
curs = conn.cursor()
# Determine starting value for sequence
curs.execute("SELECT nextval('num_data_id_seq')")
id_seq = curs.fetchone()[0]
# Make a binary file object for COPY FROM
cpy = BytesIO()
# 11-byte signature, no flags, no header extension
cpy.write(pack('!11sii', b'PGCOPY\n\377\r\n\0', 0, 0))
# Columns: id, node, ts, val1, val2
# Zip: (column position, format, size)
row_format = list(zip(range(-1, 4),
('i', 'i', 'h', 'f', 'd'),
( 4, 4, 2, 4, 8 )))
for row in data:
# Number of columns/fields (always 5)
cpy.write(pack('!h', 5))
for col, fmt, size in row_format:
value = (id_seq if col == -1 else row[col])
cpy.write(pack('!i' + fmt, size, value))
id_seq += 1 # manually increment sequence outside of database
# File trailer
cpy.write(pack('!h', -1))
# Copy data to database
cpy.seek(0)
curs.copy_expert("COPY num_data FROM STDIN WITH BINARY", cpy)
# Update sequence on database
curs.execute("SELECT setval('num_data_id_seq', %s, false)", (id_seq,))
conn.commit()
अपडेट करें
मैंने कॉपी के लिए फाइल लिखने के लिए उपरोक्त दृष्टिकोण को फिर से लिखा। पायथन में मेरा डेटा NumPy सरणियों में है, इसलिए इनका उपयोग करना समझ में आता है। यहां कुछ उदाहरण दिया गया है data
1M पंक्तियों, 7 स्तंभों के साथ:
import psycopg2
import numpy as np
from struct import pack
from io import BytesIO
from datetime import datetime
conn = psycopg2.connect("dbname=mydb user=postgres")
curs = conn.cursor()
# NumPy record array
shape = (7, 2000, 500)
print('Generating data with %i rows, %i columns' % (shape[1]*shape[2], shape[0]))
dtype = ([('id', 'i4'), ('node', 'i4'), ('ts', 'i2')] +
[('s' + str(x), 'f4') for x in range(shape[0])])
data = np.empty(shape[1]*shape[2], dtype)
data['id'] = np.arange(shape[1]*shape[2]) + 1
data['node'] = np.tile(np.arange(shape[1]) + 1, shape[2])
data['ts'] = np.repeat(np.arange(shape[2]) + 1, shape[1])
data['s0'] = np.random.rand(shape[1]*shape[2]) * 100
prv = 's0'
for nxt in data.dtype.names[4:]:
data[nxt] = data[prv] + np.random.rand(shape[1]*shape[2]) * 10
prv = nxt
मेरे डेटाबेस पर, मेरे पास दो टेबल हैं जो इस तरह दिखती हैं:
CREATE TABLE num_data_binary
(
id integer PRIMARY KEY,
node integer NOT NULL,
ts smallint NOT NULL,
s0 real,
s1 real,
s2 real,
s3 real,
s4 real,
s5 real,
s6 real
) WITH (OIDS=FALSE);
और इसी तरह की एक अन्य तालिका जिसका नाम num_data_text
. है ।
NumPy रिकॉर्ड सरणी में जानकारी का उपयोग करके COPY (पाठ और बाइनरी प्रारूप दोनों) के लिए डेटा तैयार करने के लिए यहां कुछ सरल सहायक कार्य दिए गए हैं:
def prepare_text(dat):
cpy = BytesIO()
for row in dat:
cpy.write('\t'.join([repr(x) for x in row]) + '\n')
return(cpy)
def prepare_binary(dat):
pgcopy_dtype = [('num_fields','>i2')]
for field, dtype in dat.dtype.descr:
pgcopy_dtype += [(field + '_length', '>i4'),
(field, dtype.replace('<', '>'))]
pgcopy = np.empty(dat.shape, pgcopy_dtype)
pgcopy['num_fields'] = len(dat.dtype)
for i in range(len(dat.dtype)):
field = dat.dtype.names[i]
pgcopy[field + '_length'] = dat.dtype[i].alignment
pgcopy[field] = dat[field]
cpy = BytesIO()
cpy.write(pack('!11sii', b'PGCOPY\n\377\r\n\0', 0, 0))
cpy.write(pgcopy.tostring()) # all rows
cpy.write(pack('!h', -1)) # file trailer
return(cpy)
दो कॉपी प्रारूप विधियों को बेंचमार्क करने के लिए मैं सहायक कार्यों का उपयोग कैसे कर रहा हूं:
def time_pgcopy(dat, table, binary):
print('Processing copy object for ' + table)
tstart = datetime.now()
if binary:
cpy = prepare_binary(dat)
else: # text
cpy = prepare_text(dat)
tendw = datetime.now()
print('Copy object prepared in ' + str(tendw - tstart) + '; ' +
str(cpy.tell()) + ' bytes; transfering to database')
cpy.seek(0)
if binary:
curs.copy_expert('COPY ' + table + ' FROM STDIN WITH BINARY', cpy)
else: # text
curs.copy_from(cpy, table)
conn.commit()
tend = datetime.now()
print('Database copy time: ' + str(tend - tendw))
print(' Total time: ' + str(tend - tstart))
return
time_pgcopy(data, 'num_data_text', binary=False)
time_pgcopy(data, 'num_data_binary', binary=True)
यहाँ पिछले दो time_pgcopy
. से आउटपुट है आदेश:
Processing copy object for num_data_text
Copy object prepared in 0:01:15.288695; 84355016 bytes; transfering to database
Database copy time: 0:00:37.929166
Total time: 0:01:53.217861
Processing copy object for num_data_binary
Copy object prepared in 0:00:01.296143; 80000021 bytes; transfering to database
Database copy time: 0:00:23.325952
Total time: 0:00:24.622095
तो दोनों NumPy → फ़ाइल और फ़ाइल → डेटाबेस चरण बाइनरी दृष्टिकोण के साथ तेज़ हैं। स्पष्ट अंतर यह है कि पायथन COPY फ़ाइल कैसे तैयार करता है, जो पाठ के लिए वास्तव में धीमी है। सामान्यतया, बाइनरी प्रारूप इस स्कीमा के पाठ प्रारूप के रूप में 2/3 समय में डेटाबेस में लोड हो जाता है।
अंत में, मैंने डेटाबेस के भीतर दोनों तालिकाओं के मानों की तुलना यह देखने के लिए की कि क्या संख्याएँ भिन्न थीं। लगभग 1.46% पंक्तियों में कॉलम s0
. के लिए अलग-अलग मान हैं , और s6
. के लिए यह अंश बढ़कर 6.17% हो जाता है (शायद मेरे द्वारा उपयोग की जाने वाली यादृच्छिक विधि से संबंधित)। सभी 70M 32-बिट फ्लोट मानों के बीच गैर-शून्य पूर्ण अंतर 9.3132257e-010 और 7.6293945e-006 के बीच है। टेक्स्ट और बाइनरी लोडिंग विधियों के बीच ये छोटे अंतर फ्लोट → टेक्स्ट → टेक्स्ट प्रारूप विधि के लिए आवश्यक फ्लोट रूपांतरणों से सटीकता के नुकसान के कारण हैं।