2013-06-12 10 views
44

Już trochę poszedłem i wydaje się, że nie ma satysfakcjonującej odpowiedzi na mój problem.Migracja Railsy - kolumna zmiany z konwersją typów

Mam tabelę z kolumną typu ciąg. Chciałbym uruchomić po migracji:

class ChangeColumnToBoolean < ActiveRecord::Migration 
    def up 
     change_column :users, :smoking, :boolean 
    end 
end 

Gdy uruchomię to dostaję następujący błąd

PG::Error: ERROR: column "smoking" cannot be cast automatically to type boolean 
HINT: Specify a USING expression to perform the conversion. 
: ALTER TABLE "users" ALTER COLUMN "smoking" TYPE boolean 

wiem, że mogę wykonać tę migrację przy użyciu czystego SQL ale nadal byłoby ładniej, jeśli mógł zrobić to z Railsami. Przeszedłem przez kod Rails i wydaje się, że nie ma takiej możliwości, ale może ktoś zna sposób?

Nie jestem zainteresowany: - czystym SQL - upuszczenie kolumny - tworzenie innej kolumny, konwersja danych, a następnie spada oryginalny zmiany nazwy

+0

Ale to jedyny sposób, o ile wiem .. trzeba utworzyć kolumnę, migracji i usunąć stare kolumna ... – ZedTuX

Odpowiedz

5

Odkąd używam PostgreSQL, na razie zdecydowałem się na rozwiązanie SQL. zapytań używany:

execute 'ALTER TABLE "users" ALTER COLUMN "smoking" TYPE boolean USING CASE WHEN "flatshare"=\'true\' THEN \'t\'::boolean ELSE \'f\'::boolean END' 

To działa tylko wtedy, gdy jeden ma pole wypełnione prawda/fałsz strun (takich jak domyślne radiowego gromadzenia przycisk pomocnika z wymuszonym typu boolean będzie generować)

+0

To jest złe, ponieważ za każdym razem, gdy odbudujesz swój DB, będziesz musiał ponownie uruchomić tę instrukcję SQL. –

+1

@DonnyP, jeśli umieściłem to w migracji? Srsly? –

+1

@DonnyP jeśli przez "przebudowanie bazy danych" masz na myśli "przebudowę schematu bazy danych" - zalecanym sposobem na to jest użycie 'rake db: structure: load' /' rake db: schema: load', a nie przez uruchomienie wszystkie migracje. –

34

Nie wszystkie bazy danych pozwalają na zmianę typu kolumny, ogólnie brane podejście polega na dodaniu nowej kolumny żądanego typu, przeniesieniu wszystkich danych, usunięciu starej kolumny i zmianie nazwy nowej.

add_column :users, :smoking_tmp, :boolean 

User.reset_column_information # make the new column available to model methods 
User.all.each do |user| 
    user.smoking_tmp = user.smoking == 1 ? true : false # If smoking was an int, for example 
    user.save 
end 

# OR as an update all call, set a default of false on the new column then update all to true if appropriate. 
User.where(:smoking => 1).update_all(:smoking_tmp = true) 

remove_column :users, :smoking 
rename_column :users, :smoking_tmp, :smoking 
+0

Tak, to jest rozwiązanie, ale jak powiedziałem, nie jeden szukałem. Również pod względem szybkości jest to bardzo złe - w dużej, produkcyjnej bazie danych trwało wieki. –

+1

@ MichałSzyndel Możesz użyć [update_all] (http: // apidock.com/rails/ActiveRecord/Relation/update_all) zamiast bloku, który wygeneruje tylko jedną instrukcję SQL. Dodaj przykład. – Matt

+0

Możesz również użyć opcji 'user.smoking.to_boolean', jeśli masz ciągi takie jak" Tak "i" Nie "w oryginalnej, nie boolowskiej kolumnie. Ładnie sobie z tym radzi. –

98

Jeśli twój ciągów w smoking kolumnie są już ważne wartości logiczne, następujące oświadczenie będzie zmienić typ kolumny bez utraty danych:

change_column :users, :smoking, 'boolean USING CAST(smoking AS boolean)' 

Podobnie, można użyć tego komunikatu rzucić kolumn do liczby całkowitej:

change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)' 

Używam PostgreSQL. Nie wiem, czy to rozwiązanie działa w przypadku innych baz danych.

+0

działa dla mnie - dzięki! – ncherro

+2

Aby uniknąć zamieszania: tak, podajesz swoją nową kolumnę dwukrotnie. Pomyśl o tym, potwierdzając, że chcesz to zrobić i nie udzielając instrukcji, jak zmienić foo w bool – MCB

6

więc prawo do boolean w PostgreSQL:

change_column :table_name, :field,'boolean USING (CASE field WHEN \'your any string as true\' THEN \'t\'::boolean ELSE \'f\'::boolean END)' 

i można dodać trochę więcej WHEN - THEN kondycję w swojej wypowiedzi

przypadku innych serwerów baz danych, wyrażenie zostanie zbudowana na podstawie składni twój serwer bazy danych, ale zasada jest taka sama. Tylko ręczny algorytm konwersji, całkowicie bez SQL, niestety nie jest wystarczający.

Way ze składnią change_column :table, :filed, 'boolean USING CAST(field AS boolean)' nadaje się tylko wtedy, gdy zawartość pola czegoś podobnego: true/false/zerowej

Powiązane problemy