【问题标题】:How to programmatically transfer a lot of data between tables?如何以编程方式在表之间传输大量数据?
【发布时间】:2011-08-04 02:57:28
【问题描述】:

我有两个表,第一个表有 1400 万个数据,第二个表有 150 万个数据。

所以我想知道如何将这些数据传输到另一个表进行规范化? 以及如何将某种类型转换为另一种类型,例如:我有一个名为“year”的字段,但它的类型是 varchar,但我想要它是一个整数,我该怎么做?

我曾想过在 java 中使用 JDBC 在循环中执行此操作,但我认为这不是有效的。

// 1.5 million of data
CREATE TABLE dbo.directorsmovies
    (
    movieid    INT NULL,
    directorid INT NULL,
    dname      VARCHAR (500) NULL,
    addition   VARCHAR (1000) NULL
    )

//14 million of data
CREATE TABLE dbo.movies
    (
    movieid      VARCHAR (20) NULL,
    title        VARCHAR (400) NULL,
    mvyear       VARCHAR (100) NULL,
    actorid      VARCHAR (20) NULL,
    actorname    VARCHAR (250) NULL,
    sex          CHAR (1) NULL,
    as_character VARCHAR (1500) NULL,
    languages    VARCHAR (1500) NULL,
    genres       VARCHAR (100) NULL
    )

这是我的新表格:

DROP TABLE actor
CREATE TABLE actor (
    id INT PRIMARY KEY IDENTITY,
    name VARCHAR(200) NOT NULL, 
    sex VARCHAR(1) NOT NULL
)

DROP TABLE actor_character
CREATE TABLE actor_character(
    id INT PRIMARY KEY IDENTITY,
    character VARCHAR(100)
)

DROP TABLE director
CREATE TABLE director(
    id INT PRIMARY KEY IDENTITY,
    name VARCHAR(200) NOT NULL,
    addition VARCHAR(150)
)


DROP TABLE movie
CREATE TABLE movie(
    id INT PRIMARY KEY IDENTITY,
    title VARCHAR(200) NOT NULL,
    year INT
)


DROP TABLE language
CREATE TABLE language(
    id INT PRIMARY KEY IDENTITY,
    language VARCHAR (100) NOT NULL
)

DROP TABLE genre
CREATE TABLE genre(
    id INT PRIMARY KEY IDENTITY,
    genre VARCHAR(100) NOT NULL
)

DROP TABLE director_movie
CREATE TABLE director_movie(
    idDirector INT,
    idMovie INT,
    CONSTRAINT fk_director_movie_1 FOREIGN KEY (idDirector) REFERENCES director(id),
    CONSTRAINT fk_director_movie_2 FOREIGN KEY (idMovie) REFERENCES movie(id),
    CONSTRAINT pk_director_movie PRIMARY KEY(idDirector,idMovie)
)

DROP TABLE genre_movie
CREATE TABLE genre_movie(
    idGenre INT,
    idMovie INT,
    CONSTRAINT fk_genre_movie_1 FOREIGN KEY (idMovie) REFERENCES movie(id),
    CONSTRAINT fk_genre_movie_2 FOREIGN KEY (idGenre) REFERENCES genre(id),
    CONSTRAINT pk_genre_movie PRIMARY KEY (idMovie, idGenre)
)

DROP TABLE language_movie
CREATE TABLE language_movie(
    idLanguage INT,
    idMovie INT,
    CONSTRAINT fk_language_movie_1 FOREIGN KEY (idLanguage) REFERENCES language(id),
    CONSTRAINT fk_language_movie_2 FOREIGN KEY (idMovie) REFERENCES movie(id),
    CONSTRAINT pk_language_movie PRIMARY KEY (idLanguage, idMovie)  
)

DROP TABLE movie_actor
CREATE TABLE movie_actor(
    idMovie INT,
    idActor INT,
    CONSTRAINT fk_movie_actor_1 FOREIGN KEY (idMovie) REFERENCES movie(id),
    CONSTRAINT fk_movie_actor_2 FOREIGN KEY (idActor) REFERENCES actor(id),
    CONSTRAINT pk_movie_actor PRIMARY KEY (idMovie,idActor)
)

更新: 我正在使用 SQL Server 2008。 抱歉各位,我忘了说是不同的数据库:

未归一化的是调用学科db,我归一化的是调用imdb。

最好的问候, 瓦尔特·恩里克。

【问题讨论】:

  • 你在处理什么数据库?您可以发布相关表格的 DDL 吗?
  • 最有效的方法是使用您正在使用的 SQL 引擎原生的一些批量插入功能。
  • 是的,有些数据库允许您根据查询结果填充表。您使用的是哪个数据库?
  • @sjr,我使用的是 SQL Server 2008。

标签: java sql-server database servlets


【解决方案1】:

我最近刚刚为约 150 Gb 的数据执行此操作。我为每个表使用了一对合并语句。第一个合并语句说“如果它不在目标表中,则将其复制到那里”,第二个表示“如果它在目标表中,则将其从源表中删除”。我把两者都放在一个while循环中,每次只做10000行。将其保存在服务器上(而不是通过客户端传输)将极大地提高性能。试一试!

【讨论】:

    【解决方案2】:

    虽然我的表与你的相比是小巫见大巫,但我曾经用存储过程解决过这种问题。对于 MySQL,以下是我的脚本的简化(未经测试)本质,但类似的东西应该适用于所有主要的 SQL 库。

    首先,您应该只添加一个新的整数年份列(例如 int_year),然后使用以下过程遍历所有行:

    DROP PROCEDURE IF EXISTS move_data;
    CREATE PROCEDURE move_data()
    BEGIN
      DECLARE done INT DEFAULT 0;
      DECLARE orig_id INT DEFAULT 0;
      DECLARE orig_year VARCHAR DEFAULT "";
      DECLARE cur1 CURSOR FOR SELECT id, year FROM table1;
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
      OPEN cur1;
    
      PREPARE stmt FROM "UPDATE table1 SET int_year = ? WHERE id = ?";
    
      read_loop: LOOP
        FETCH cur1 INTO orig_id, orig_year;
        IF done THEN
          LEAVE read_loop;
        END IF;
    
        SET @year= orig_year;
        SET @id = orig_id;
    
        EXECUTE stmt USING @orig_year, @id;
      END LOOP;
      CLOSE cur1;
    END;
    

    要开始这个过程,只需CALL move_data()

    上面的SQL有两个加快速度的主要思路:

    1. 使用 CURSORS 遍历大表
    2. 使用 PREPARED 语句快速执行预先知道的命令

    PS。就我而言,这将事情从几秒钟加速到几秒钟,但在你的情况下,它仍然需要相当长的时间。因此,最好从命令行执行,而不是某些 Web 界面(例如 PhpMyAdmin)。

    【讨论】:

    • 注意 - 上面的查询过于简化,并没有显示 PROCEDURES 的全部功能,但您明白了,可以向其中添加自己的任务。
    • 要走的路;庞大的数据量 + 复杂的逻辑需要在数据库服务器上执行。
    • 谢谢你的帮助,但是我有两个不同的数据库,在这种情况下我该怎么做?
    • @Valter - 这篇文章describes how you can select from multiple databases in MS SQL。基本上,您在新数据库中创建 PROCEDURE 并在 SELECT 查询中使用旧数据库名称添加表名:DECLARE cur1 CURSOR FOR SELECT movieid, mvyear FROM disciplinedb.dbo.movies;
    【解决方案3】:

    如果两个表都在同一个数据库中,那么最有效的传输是在数据库中进行,最好是发送一条 SQL 语句在数据库中执行。

    应避免将数据从 d/b 服务器移动到其他地方,然后再返回到 d/b 服务器,除非有原因只能在服务器外进行转换。如果目标是不同的服务器,那么这不是问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-18
      • 2018-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多