SQL Server: как улучшить запрос вставки? - PullRequest
0 голосов
/ 14 апреля 2011

У меня есть одна база данных, которая не нормализована:

schollinabd.movies:

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
    )

И у меня есть моя база данных: labbd11, где я собираюсь нормализовать эти данные из дисциплинарного шкафа.Поэтому я пытаюсь выполнить этот запрос:

INTO labbd11..movie_actor(idMovie, idActor, idCharacter) 
SELECT CASE 
         WHEN IsNumeric(movies.movieid+ '.0e0') <> 1  THEN NULL 
         ELSE CAST (movies.movieid AS INT) 
       END, 
       CASE WHEN IsNumeric(movies.actorid+ '.0e0') <> 1  THEN NULL 
            ELSE CAST (movies.actorid AS INT) 
       END, 
      (SELECT id FROM actor_character WHERE character = movies.as_character) 
FROM disciplinabd..movies

Он выполняется нормально, но это огромное количество данных, где я должен это сделать, например, 14 миллионов строк в schollinabd.movies.

Мои вопросы:

  1. Есть ли способ улучшить мою вставку?
  2. Могу ли я вставить что-то вроде insert (1, 1000) ... после завершения, я просто изменяю значения, такие как insert( 1001, 2000) ..и продолжай.Что я имею в виду, есть ли какая-нибудь возможность вставить мою базу данных понемногу?Таким образом, я могу избежать операции отката, если соединение разорвано.Вчера этот запрос вставки выполнялся в течение 16 часов, затем соединение разорвалось, и я потерял всю работу.

ОБНОВЛЕНИЕ

CREATE TABLE movie(
    id INT PRIMARY KEY,
    title VARCHAR(400) NOT NULL,
    year INT
)

CREATE TABLE actor (
    id INT PRIMARY KEY,
    name VARCHAR(250) NOT NULL, 
    sex CHAR(1) NOT NULL
)

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

CREATE TABLE movie_actor(
    idMovie INT,
    idActor INT,
    idCharacter INT,
    CONSTRAINT fk_movie_actor_1 FOREIGN KEY (idMovie) REFERENCES movie(id) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT fk_movie_actor_2 FOREIGN KEY (idActor) REFERENCES actor(id) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT fk_movie_actor_3 FOREIGN KEY (idCharacter) REFERENCES actor_character(id) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT pk_movie_actor PRIMARY KEY (idMovie,idActor, idCharacter)
)

Ответы [ 2 ]

1 голос
/ 14 апреля 2011

Вы не видели, какую СУБД вы используете, что может помочь нам более точно ответить на ваш вопрос, но чтобы ответить на второй вопрос, вы, скорее всего, можете ограничить свой запрос SELECT, чтобы повлиять на объем вставляемых данных.Например,

INSERT INTO labbd11..movie_actor(idMovie, idActor, idCharacter) 
SELECT CASE 
         WHEN IsNumeric(movies.movieid+ '.0e0') <> 1  THEN NULL 
         ELSE CAST (movies.movieid AS INT) 
       END, 
       CASE WHEN IsNumeric(movies.actorid+ '.0e0') <> 1  THEN NULL 
            ELSE CAST (movies.actorid AS INT) 
       END, 
      (SELECT id FROM actor_character WHERE character = movies.as_character) 
FROM disciplinabd..movies
WHERE movieid >= 1000 and movieid < 2000

Если у вас нет непрерывного диапазона идентификаторов, вы можете сгенерировать его, но метод будет зависеть от конкретной базы данных, которую вы используете.

Какдля вашего первоначального вопроса о том, как улучшить производительность, я бы начал с перемещения подвыбора в JOIN и обеспечения правильного индекса в actor_character.Например:

INTO labbd11..movie_actor(idMovie, idActor, idCharacter) 
SELECT CASE 
         WHEN IsNumeric(movies.movieid+ '.0e0') <> 1  THEN NULL 
         ELSE CAST (movies.movieid AS INT) 
       END, 
       CASE WHEN IsNumeric(movies.actorid+ '.0e0') <> 1  THEN NULL 
            ELSE CAST (movies.actorid AS INT) 
       END, 
      actor_character.id 
FROM disciplinabd..movies
LEFT JOIN disciplinabd..actor_characture ON movies.as_character = actor_characture.character
WHERE movieid >= 1000 and movieid < 2000

Опять же, если вы можете явно указать, какую базу данных вы используете, мы можем предоставить более точный ответ.Если бы я писал что-то подобное, я бы не ожидал, что выполнение 14 миллионов строк займет более нескольких минут на оборудовании серверного класса.

0 голосов
/ 14 апреля 2011

16 часов - это очень много времени для вставки только 14 миллионов строк. Я не знаю, на что похоже ваше оборудование, поэтому я просто отвечу на вопрос. С 14 миллионами строк будет намного медленнее, если вы откроете соединение для каждой 1000, поэтому я бы предложил более переменное число.

Я также предлагаю добавить указатель к movieid, если можете.

create nonclustered index IX_movies on movies(movieid)

Вы можете использовать цикл while для выполнения того, что вы ищете.

Declare @loopMax int,@bottomRange int,@topRange int,@rangeSize int
select @loopMax = MAX(movies.movieid) from disciplinabd..movies
set @rangeSize = @loopMax/20
set @bottomRange = 0
set @topRange = @rangeSize
while @topRange < @loopMax
begin
    INSERT INTO labbd11..movie_actor(idMovie, idActor, idCharacter) 
    SELECT CASE 
        WHEN IsNumeric(movies.movieid+ '.0e0') <> 1  THEN NULL 
        ELSE CAST (movies.movieid AS INT) 
   END, 
   CASE WHEN IsNumeric(movies.actorid+ '.0e0') <> 1  THEN NULL 
        ELSE CAST (movies.actorid AS INT) 
   END, 
   actor_character.id 
   FROM disciplinabd..movies
   LEFT JOIN actor_character ON movies.as_character = actor_character.character
   WHERE movieid >= @bottomRange and movieid < @topRange
   set @bottomRange = @topRange
   set @topRange = @topRange + @rangeSize
end    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...