使用Python将CSV条目与postgresql表中的条目进行比较

时间:2018-05-06 12:34:16

标签: python postgresql csv

我正在使用Postgresql 10,我需要在CSV文件中进行查找,并将CSV文件中的条目与postgres表中的条目进行比较。数据库看起来像这样,我必须在域表中插入域名并在rank表中排名:

CREATE TABLE lists (list_id integer PRIMARY KEY,
                    list_name text);

CREATE TABLE domains (domain_id BIGSERIAL PRIMARY KEY,
                      domain_name text UNIQUE);

CREATE TABLE ranks (list_id integer REFERENCES lists,
                    domain_id integer REFERENCES domains,
                    rank integer,
                    date date,
                    PRIMARY KEY (list_id, rank, date));

csv包含两个条目,排名和域名,如下所示:“1,google.com”

目前我将域名插入到域表中,其中域ID自动递增并用作主键。然后我想将排名插入到排名表中。但是我正在努力将domain_id从domains表中获取到rank表中,因为domain_id在rank表中充当外键。所以我想检查CSV的域名,检查域表,并在插入排名时为每个域输出domain_id。因此,每个域名可以有多个等级,这与日期不同。

我现在使用的当前脚本如下所示:

    import tkinter as tk
    from tkinter import filedialog
    import csv
    import psycopg2
    import shutil as sh

    root = tk.Tk()
    root.withdraw()
    file_path = filedialog.askopenfilename()
    new_path = 'C:/Users/%user%/Desktop/alexa-top1m_16042018.csv'

    conn = psycopg2.connect("host=localhost dbname=test user=postgres   password=test")
    cur = conn.cursor()

    sh.copy2(file_path, new_path)
    with open(new_path, 'r') as original: data = original.read()
    with open(new_path, 'w') as modified: modified.write("rank,domain_name\n" + data)

    with open(new_path, 'r') as f:
        reader = csv.DictReader(f)


    for row in reader:
        cur.execute(
           """INSERT INTO ranks (list_id, rank, date) VALUES (%s, %s, %s);""", ( 1, row['rank'], '2018-04-16',)
        )

   conn.commit()

我使用psycopg2连接数据库并进行查询。

有谁知道如何做到这一点,或者对如何实现这一点有任何其他建议?

1 个答案:

答案 0 :(得分:1)

您可以创建一个临时表来保存CSV数据并使用SQL查询在domainsranks表中插入数据。

以下是临时表的代码:

CREATE TABLE temporary_table (
  rank INTEGER,
  domain TEXT
);

使用CSV数据填写此表。

现在,插入CSV文件中但不存在于domains表中的域。

INSERT INTO domains (domain_name)
  (SELECT DISTINCT domain as domain_name FROM temporary_table
    EXCEPT
  SELECT domain_name FROM domains);

现在,当您拥有domains表中的所有现有域时,我们可以在ranks表中插入行。

INSERT INTO ranks (list_id, domain_id, rank, date)
    SELECT 1 as list_id, d.domain_id, rank, now()::DATE 
    FROM temporary_table tt JOIN domains d ON tt.domain = d.domain_name;

为了获取我们要插入的排名的域ID,我们按域名在temporary_tabledomains表之间进行连接。这样,我们就可以找到每个等级的domain_id。

请注意,我在排名插入中添加了1 list_idnow()::date,因为您没有提供应从中提取数据的列。

另外,请注意组合的主键PRIMARY KEY (list_id, rank, date)。 如果要在同一日期插入多个域的排名,并且某些域具有相同的rank和list_id值,则会出现重复的键值错误,并且不会插入数据。 为了解决这个问题,您还可以在组合主键中添加domain_id。