2011-03-17 20 views
1

Я импортировал некоторые данные, используя приложение, которое собирает информацию из IMDB и передает их в базу данных MYSQL.MYSQL - Разделить данные на несколько строк

кажется поля не были нормализованы и содержали множество значений в 1 поле

Например:

Table Movie 
MovieID   Movie_Title   Written_By 
1    Movie1    Person1, Person2 
2    Movie2    Person3 
3    Movie3    Person4, Person2, Person6 

Есть ли способ, чтобы отделить ценности и их вставить в другую таблицу, чтобы что-то вроде это и без каких-либо дубликатов?

Table Writers 
WriterID   Written_By    MovieId  
1    Person1     1 
2    Person2     1 
3    Person3     3 

Я сделал несколько поисковых запросов и обнаружил, что я должен обрабатывать эти данные с помощью PHP. Но у меня нет знаний с PHP вообще.

Следует ли преобразовать эти данные, используя только MYSQL?

+0

Будете ли вы продолжать получать такой канал? Или это будет одноразовая операция? Кроме того, в каком формате были исходные данные, извлекаемые из источника (до того, как он был введен в MySQL). Скорее всего, другой подход к загрузке таблиц MySql - это то, что вам нужно. – Jai

+0

дублировать? Http: // StackOverflow.com/questions/3936088/mysql-split-comma-separated-list-in-multiple-rows – TheSean

+0

Также прочитайте это - http://stackoverflow.com/questions/1096679/can-mysql-split-a-column – Jai

ответ

2

Вы можете использовать хранимую процедуру, которая использует курсор для решения этой проблемы, но она не очень изящна, но не является списком авторов, разделенных запятыми!

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

Надежда это помогает :)

mysql> select * from movies_unf; 
+---------+-------------+------------------------------------------------------+ 
| movieID | movie_title | written_by           | 
+---------+-------------+------------------------------------------------------+ 
|  1 | movie1  | person1, person2          | 
|  2 | movie2  | person3            | 
|  3 | movie3  | person4, person2, person6       | 
|  4 | movie4  | person4, person4, person1, person2, person1,person8, | 
|  5 | movie1  | person1, person2          | 
+---------+-------------+------------------------------------------------------+ 
5 rows in set (0.00 sec) 

call normalise_movies_unf(); 

mysql> select * from movies; 
+----------+--------+ 
| movie_id | title | 
+----------+--------+ 
|  1 | movie1 | 
|  2 | movie2 | 
|  3 | movie3 | 
|  4 | movie4 | 
+----------+--------+ 
4 rows in set (0.00 sec) 

mysql> select * from writers; 
+-----------+---------+ 
| writer_id | name | 
+-----------+---------+ 
|   1 | person1 | 
|   2 | person2 | 
|   3 | person3 | 
|   4 | person4 | 
|   6 | person6 | 
|  12 | person8 | 
+-----------+---------+ 
6 rows in set (0.00 sec) 

mysql> select * from movie_writers; 
+----------+-----------+ 
| movie_id | writer_id | 
+----------+-----------+ 
|  1 |   1 | 
|  1 |   2 | 
|  2 |   3 | 
|  3 |   2 | 
|  3 |   4 | 
|  3 |   6 | 
|  4 |   1 | 
|  4 |   2 | 
|  4 |   4 | 
|  4 |  12 | 
+----------+-----------+ 
10 rows in set (0.00 sec) 

Пример таблицы

drop table if exists movies_unf; 
create table movies_unf 
(
movieID int unsigned not null primary key, 
movie_title varchar(255) not null, 
written_by varchar(1024) not null 
)engine=innodb; 

insert into movies_unf values 
(1,'movie1','person1, person2'), 
(2,'movie2','person3'), 
(3,'movie3','person4, person2, person6'), 
(4,'movie4','person4, person4, person1, person2, person1,person8,'), -- dodgy writers 
(5,'movie1','person1, person2'); -- dodgy movie 

drop table if exists movies; 
create table movies 
(
movie_id int unsigned not null auto_increment primary key, 
title varchar(255) unique not null 
)engine=innodb; 

drop table if exists writers; 
create table writers 
(
writer_id int unsigned not null auto_increment primary key, 
name varchar(255) unique not null 
)engine=innodb; 

drop table if exists movie_writers; 
create table movie_writers 
(
movie_id int unsigned not null, 
writer_id int unsigned not null, 
primary key (movie_id, writer_id) 
)engine=innodb; 

Хранимые процедуры

drop procedure if exists normalise_movies_unf; 

delimiter # 

create procedure normalise_movies_unf() 
begin 

declare v_movieID int unsigned default 0; 
declare v_movie_title varchar(255); 
declare v_writers varchar(1024); 

declare v_movie_id int unsigned default 0; 
declare v_writer_id int unsigned default 0; 
declare v_name varchar(255); 

declare v_csv_done tinyint unsigned default 0; 
declare v_csv_idx int unsigned default 0; 

declare v_done tinyint default 0; 
declare v_cursor cursor for 
    select distinct movieID, movie_title, written_by from movies_unf; 

declare continue handler for not found set v_done = 1; 

start transaction; 

open v_cursor; 
repeat 
    fetch v_cursor into v_movieID, v_movie_title, v_writers; 

    set v_movie_title = trim(v_movie_title); 
    set v_writers = replace(v_writers,' ', ''); 

    -- insert the movie 
    insert ignore into movies (title) values (v_movie_title); 
    select movie_id into v_movie_id from movies where title = v_movie_title; 

    -- split the out the writers and insert 
    set v_csv_done = 0;  
    set v_csv_idx = 1; 

    while not v_csv_done do 
    set v_name = substring(v_writers, v_csv_idx, 
     if(locate(',', v_writers, v_csv_idx) > 0, 
     locate(',', v_writers, v_csv_idx) - v_csv_idx, 
     length(v_writers))); 

     set v_name = trim(v_name); 

     if length(v_name) > 0 then 
     set v_csv_idx = v_csv_idx + length(v_name) + 1; 

     insert ignore into writers (name) values (v_name); 
     select writer_id into v_writer_id from writers where name = v_name; 
     insert ignore into movie_writers (movie_id, writer_id) values (v_movie_id, v_writer_id); 
     else 
     set v_csv_done = 1; 
     end if; 

    end while; 

until v_done end repeat; 
close v_cursor; 

commit; 

truncate table movies_unf; 

end# 

delimiter ; 

РЕДАКТИРОВАТЬ

Измененный sproc, так что он не пропускает значения ключа!

drop procedure if exists normalise_movies_unf; 

delimiter # 

create procedure normalise_movies_unf() 
begin 

declare v_movieID int unsigned default 0; 
declare v_movie_title varchar(255); 
declare v_writers varchar(1024); 

declare v_movie_id int unsigned default 0; 
declare v_writer_id int unsigned default 0; 
declare v_name varchar(255); 

declare v_csv_done tinyint unsigned default 0; 
declare v_csv_idx int unsigned default 0; 

declare v_done tinyint default 0; 
declare v_cursor cursor for 
    select distinct movieID, movie_title, written_by from movies_unf; 

declare continue handler for not found set v_done = 1; 

start transaction; 

open v_cursor; 
repeat 
    fetch v_cursor into v_movieID, v_movie_title, v_writers; 

    set v_movie_title = trim(v_movie_title); 
    set v_writers = replace(v_writers,' ', ''); 

    -- insert the movie 

    if not exists (select 1 from movies where title = v_movie_title) then 
    insert ignore into movies (title) values (v_movie_title); 
    end if; 
    select movie_id into v_movie_id from movies where title = v_movie_title; 

    -- split the out the writers and insert 
    set v_csv_done = 0;  
    set v_csv_idx = 1; 

    while not v_csv_done do 
    set v_name = substring(v_writers, v_csv_idx, 
     if(locate(',', v_writers, v_csv_idx) > 0, 
     locate(',', v_writers, v_csv_idx) - v_csv_idx, 
     length(v_writers))); 

     set v_name = trim(v_name); 

     if length(v_name) > 0 then 
     set v_csv_idx = v_csv_idx + length(v_name) + 1; 


     if not exists (select 1 from writers where name = v_name) then 
      insert ignore into writers (name) values (v_name); 
     end if; 
     select writer_id into v_writer_id from writers where name = v_name; 
     insert ignore into movie_writers (movie_id, writer_id) values (v_movie_id, v_writer_id); 
     else 
     set v_csv_done = 1; 
     end if; 

    end while; 

until v_done end repeat; 
close v_cursor; 

commit; 

truncate table movies_unf; 

end# 

delimiter ; 
+0

Спасибо за код! Есть небольшая проблема. Файл writer_id из таблицы авторов не является инкрементным. 3,4,6,12 и т. Д. – huor11

+0

, что не имеет значения - он по-прежнему является уникальным ключом, и причина, по которой он пропускает значения, заключается в том, что я использую вставку ignore против проверки, чтобы увидеть, существует ли писатель перед вставкой - легко изменить, если вы считаете, что это проблема –

+0

см. edit - новый код sproc :) –

0

MySQL не особенно хорош для струнных манипуляций такого рода. Скорее всего, вам будет гораздо проще объединить данные с помощью обычного языка программирования (perl, php, ruby, python и т. Д.), Которые имеют гораздо более надежные функции текстового ввода.

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

Alice,Eve,Bob 

легко разделить на запятой, но как насчет

Alice,Eve,Esquire.,Bob 
0

К сожалению, нет никакой функции строки расщепления в MySQL. Вот related post (не совсем ваш дубликат) с решением, которое разбивает строку на несколько столбцов.

Смежные вопросы