【问题标题】:PostgreSQL multiple CSV import and add filename to each columnPostgreSQL 多个 CSV 导入并为每列添加文件名
【发布时间】:2017-04-13 18:57:28
【问题描述】:

我有 200k csv 文件,我需要将它们全部导入到一个 postgresql 表中。它是来自各种设备的参数列表,每个 csv 的文件名都包含设备的序列号,我需要它位于每一行的列之一中。

所以为了简化,我有几列数据(没有标题),假设每个 csv 文件中的列是:日期、变量、值和文件名包含 SERIALNUMBER_and_someOtherStuffIDontNeed.csv

我正在尝试使用 cygwin 编写一个 bash 脚本来迭代文件并为我执行此操作,但是由于某种原因它不起作用,显示“在“as”处或附近出现语法错误'

这是我的代码:

#!/bin/bash
FILELIST=/cygdrive/c/devices/files/*
for INPUT_FILE in $FILELIST
do
psql -U postgres -d devices -c "copy devicelist
(
Date,
Variable,
Value,
SN as CURRENT_LOAD_SOURCE(),
)
from '$INPUT_FILE
delimiter ',' ;"
done

我正在学习 SQL,所以这可能是一个明显的错误,但我看不到它。

我还知道,在这种形式下,我将获得完整的文件名,而不仅仅是我想要的序列号位,但我以后可能会以某种方式处理它。

请指教。

谢谢。

【问题讨论】:

  • 您不能将所有输入合并到一个格式正确的文件中,并使用类似bcp 的实用程序在一次操作中加载它吗?这将非常缓慢。在任何情况下,您的 for 循环都会失败,因为 * 将扩展为 [Arg list too long] 错误。使用find . | xargs awk ' ...' >> allInOneFile.txt 创建您的文件。写这篇文章是一个小型的咨询活动。祝你好运。
  • 好点@shellter - 有一些简单的方法可以从命令行组合多个文本文件。
  • copy ... from program 'sed "s/$/,$INPUT_FILE/" $INPUT_FILE' ...
  • 感谢您的 cmets,我实际上正在创建一个格式正确的 csv,但是它需要很长时间......它从昨天开始运行,我估计它会花费至少再过一天左右,因此这并不是最好的解决方案。
  • 不要认为一次加载文件 1 条记录会更快 ;-/ 。除非您的文件是 TB 大小,或者您仍在 Pentium 386 上运行,否则您创建“一个正确格式的 csv”的方式几乎肯定有问题。通常,处理所需的时间少于写入磁盘的时间。在一个体面的环境中,每分钟 1 GB 是旋转磁盘的合理基准。您可以发布一个带有格式问题的新 Q,进行修复,然后在当前解决方案完成之前重新运行所有文件;-)!?祝你好运!!

标签: mysql bash postgresql python-3.x csv


【解决方案1】:

我认为 postgres 中没有 CURRENT_LOAD_SOURCE() 函数。一种解决方法是在复制时将 name-column 保留为 NULL,并且在复制之后将 patch 设置为所需的值。我更喜欢 shell here-document,因为这样可以更轻松地在 SQL 正文中进行引用。 (顺便说一句:对于 10K 的文件,获取 FILELIST 所需的 globbing 可能超过 shell 的 argmax ...)


#!/bin/bash

FILELIST="`ls /tmp/*.c`"

for INPUT_FILE in $FILELIST
do
echo "File:" $INPUT_FILE

psql -U postgres -d devices <<OMG

  -- I have a schema "tmp" for testing purposes    
CREATE TABLE IF NOT EXISTS tmp.filelist(name text, content text);

COPY tmp.filelist ( content)
from '/$INPUT_FILE' delimiter ',' ;

UPDATE tmp.filelist SET name = '$FILELIST'
WHERE name IS NULL;
OMG

完成

【讨论】:

    【解决方案2】:

    对于任何对答案感兴趣的人,我使用了一个 python 脚本来更改文件名,然后使用另一个脚本使用 psycopg2 连接到数据库,然后在一个连接中完成所有操作。花了 10 分钟而不是 10 小时。

    代码如下:

    重命名文件(显然,要从 CSV 导入,您需要填写所有行,并且我需要的信息无论如何都在前 4 列中,因此我已经制定了一个解决方案来生成全新的 CSV,而不仅仅是重命名它们):

    import os
    import csv
    
    path='C:/devices/files'
    
    os.chdir(path)
    i=0
    
    for file in os.listdir(path):
        try:
    
            i+=1
    
            if i%10000 == 0:
                #just to see the progress
                print(i)
    
            serial_number = (file[:8])
            creader = csv.reader(open(file))
            cwriter = csv.writer(open('processed_'+file, 'w'))
    
            for cline in creader:
                new_line = [val for col, val in enumerate(cline) if col not in (4, 5, 6, 7)]
                new_line.insert(0, serial_number)
                #print(new_line)
                cwriter.writerow(new_line)
    
        except:
            print('problem with file: ' + file)
            pass
    

    更新数据库:

    import os
    import psycopg2
    
    
    path="C:\\devices\\files"
    directory_listing = os.listdir(path)
    
    conn = psycopg2.connect("dbname='devices' user='postgres' host='localhost'")
    cursor = conn.cursor()
    print(len(directory_listing))
    i=100001
    
    while i < 218792:
        current_file=(directory_listing[i])
        i+=1
        full_path = "C:/devices/files/" + current_file
        with open(full_path) as f:
        cursor.copy_from(file=f, table='devicelistlive', sep=",")
        conn.commit()
    
    conn.close()
    

    不要介意while 和奇怪的数字,这只是因为我为了测试目的而分批进行。可以轻松替换为for

    【讨论】:

      猜你喜欢
      • 2020-08-31
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 2021-11-10
      • 2021-07-15
      • 1970-01-01
      • 2017-08-03
      相关资源
      最近更新 更多