Исправление плохого дизайна базы данных BAD, когда данные в системе - PullRequest
7 голосов
/ 17 сентября 2008

Я знаю, что это не вопрос ... во всяком случае, ЗДЕСЬ вопрос.

Я унаследовал базу данных с 1 (одной) таблицей, которая выглядит примерно так. Его цель - записать, какие виды встречаются в различных (200 странных) странах.

ID 
Species
Afghanistan
Albania
Algeria
American Samoa
Andorra
Angola
....
Western Sahara
Yemen
Zambia
Zimbabwe

Пример данных будет примерно таким

id Species Afghanistan Albania American Samoa
1  SP1         null     null        null
2  SP2          1         1         null
3  SP3         null      null         1

Мне кажется, что это типичная ситуация для многих, и я хочу 3 таблицы. Виды, Страна и ВидыFoundInCountry

Таблица ссылок (SpeciesFoundInCountry) будет иметь внешние ключи в таблицах видов и стран.

(Сложно нарисовать диаграмму!)

Species
SpeciesID  SpeciesName

Country
CountryID CountryName

SpeciesFoundInCountry
CountryID SpeciesID

Есть ли волшебный способ, которым я могу сгенерировать оператор вставки, который будет получать CountryID из новой таблицы Country на основе имени столбца и SpeciesID, где в исходной мега таблице есть 1?

Я могу сделать это для одной страны (это выбор, чтобы показать, что я хочу)

SELECT Species.ID, Country.CountryID
FROM Country, Species
WHERE (((Species.Afghanistan)=1)) AND (((Country.Country)="Afghanistan"));

(мега стол называется видом)

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

Есть ли способ сделать это в SQL?

Полагаю, я могу ИЛИ загрузить свои предложения where вместе и написать скрипт для создания sql, хотя это выглядит не элегантно!

Есть мысли (или требуется уточнение)?

Ответы [ 20 ]

1 голос
/ 17 сентября 2008

@ топать:

Над окном, где вы вводите ответ, есть несколько кнопок. Тот, который является 101010, является примером кода. Вы выбираете весь свой текст, который является кодом, и затем нажимаете эту кнопку. Тогда это не сильно мешает.

cout>>"I don't know C"
cout>>"Hello World"
1 голос
/ 13 октября 2008

Это (надеюсь) одноразовое упражнение, поэтому не элегантное решение может быть не таким плохим, как кажется.

Проблема (как, я уверен, вы слишком осведомлены!) Заключается в том, что в какой-то момент в вашем запросе вы должны перечислить все эти столбцы. :( Вопрос в том, каков самый элегантный способ сделать это? Ниже моя попытка. Это выглядит громоздко, потому что есть так много столбцов, но это может быть то, что вы ищете, или, по крайней мере, это может указать вам в правильное направление.

Возможное решение SQL:

/* if you have N countries */
CREATE TABLE Country
(id    int, 
 name  varchar(50)) 

INSERT Country
      SELECT 1, 'Afghanistan'
UNION SELECT 2, 'Albania', 
UNION SELECT 3, 'Algeria' ,
UNION SELECT 4, 'American Samoa' ,
UNION SELECT 5, 'Andorra' ,
UNION SELECT 6, 'Angola' ,
...
UNION SELECT N-3, 'Western Sahara', 
UNION SELECT N-2, 'Yemen', 
UNION SELECT N-1, 'Zambia', 
UNION SELECT N, 'Zimbabwe', 



CREATE TABLE #tmp
(key        varchar(N),  
 country_id int) 
/* "key" field needs to be as long as N */  


INSERT #tmp 
SELECT '1________ ... _', 'Afghanistan' 
/* '1' followed by underscores to make the length = N */

UNION SELECT '_1_______ ... ___', 'Albania'
UNION SELECT '__1______ ... ___', 'Algeria'
...
UNION SELECT '________ ... _1_', 'Zambia'
UNION SELECT '________ ... __1', 'Zimbabwe'

CREATE TABLE new_table
(country_id int, 
species_id int) 

INSERT new_table
SELECT species.id, country_id
FROM   species s , 
       #tmp    t
WHERE  isnull( s.Afghanistan, ' ' ) +  
       isnull( s.Albania, ' ' ) +  
       ... +  
       isnull( s.Zambia, ' ' ) +  
       isnull( s.Zimbabwe, ' ' ) like t.key 

Мое предложение

Лично я бы этого не делал. Я бы сделал быстрое и грязное решение, подобное тому, на которое вы ссылаетесь, за исключением того, что я бы жестко закодировал идентификаторы страны (потому что вы собираетесь делать это только один раз, верно? И вы можете сделать это сразу после создания таблицы стран, чтобы вы знали, что это за идентификаторы):

INSERT new_table SELECT Species.ID, 1 FROM Species WHERE Species.Afghanistan = 1 
INSERT new_table SELECT Species.ID, 2 FROM Species WHERE Species.Albania= 1 
...
INSERT new_table SELECT Species.ID, 999 FROM Species WHERE Species.Zambia= 1 
INSERT new_table SELECT Species.ID, 1000 FROM Species WHERE Species.Zimbabwe= 1 
1 голос
/ 17 сентября 2008

Возможно, вы захотите создать таблицы замены на месте. Тип сценария зависит от языка сценариев, который у вас есть, но вы сможете создать таблицу идентификаторов стран, просто перечислив столбцы таблицы, которая у вас есть сейчас. Как только вы это сделаете, вы можете выполнить некоторые строковые замены, чтобы просмотреть все уникальные названия стран и вставить их в таблицу видамиFoundInCountry, где столбец данной страны не равен нулю.

1 голос
/ 17 сентября 2008

Я бы определенно согласился с вашим предложением написать небольшой скрипт для создания вашего SQL с запросом для каждого столбца.

На самом деле ваш сценарий, возможно, уже был закончен за время, которое вы потратили на размышления об этом магическом запросе (который вы будете использовать только один раз, а затем выбрасываете, так какая польза в том, чтобы сделать все это волшебным и совершенным)

1 голос
/ 17 сентября 2008

Если это SQL Server, вы можете использовать таблицу sys.columns, чтобы найти все столбцы исходной таблицы. Затем вы можете использовать динамический SQL и команду pivot, чтобы делать то, что вы хотите. Ищите их в Интернете на предмет синтаксиса.

1 голос
/ 17 сентября 2008

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

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

1 голос
/ 17 сентября 2008

В SQL Server будет создан ваш собственный выбор, который вы демонстрируете. Вы можете экстраполировать на вставку

select 
  'SELECT Species.ID, Country.CountryID FROM Country, Species WHERE (((Species.' + 
 c.name + 
 ')=1)) AND (((Country.Country)="' +
 c.name + 
 '"))'
from syscolumns c
inner join sysobjects o
on o.id = c.id
where o.name = 'old_table_name'
1 голос
/ 17 сентября 2008

Вероятно, вы могли бы проявить смекалку и запросить системные столбцы для имен столбцов, а затем создать динамическую строку запроса для выполнения, но, честно говоря, это, вероятно, будет уродливее, чем быстрый скрипт для генерации операторов SQL для вас.

Надеюсь, у вас не так уж много динамического кода SQL, который обращается к старым таблицам, скрытым в вашей кодовой базе. Это может быть действительно трудная часть.

1 голос
/ 22 октября 2008

Когда я сталкивался с подобными проблемами, мне было удобно генерировать сценарий, который генерирует сценарии SQL. Вот образец, который вы дали, абстрагированный от использования% PAR1% вместо Афганистана.

SELECT Species.ID, Country.CountryID
FROM Country, Species
WHERE (((Species.%PAR1%)=1)) AND (((Country.Country)="%PAR1%"))
UNION

Также было добавлено ключевое слово union для объединения всех выбранных элементов.

Далее вам нужен список стран, сгенерированный из ваших существующих данных:

Афганистан Албания , , .

Далее вам нужен скрипт, который может перебирать список стран, и для каждой итерации, производить вывод, который заменяет Афганистан на% PAR1% на первой итерации, Албанию - на второй итерации и так далее. Алгоритм подобен слиянию почты в текстовом процессоре. Это небольшой труд, чтобы написать этот сценарий. Но, как только вы его получите, вы можете использовать его в десятках одноразовых проектов, подобных этому.

Наконец, вам нужно вручную заменить последний «UNION» на точку с запятой.

Если вы можете получить Access для выполнения этого гигантского объединения, вы можете получить нужные данные в нужной форме и вставить их в новую таблицу.

1 голос
/ 17 сентября 2008

Я бы сделал это трехступенчатым процессом с небольшим временным изменением вашей таблицы SpeciesFoundInCountry. Я бы добавил столбец в эту таблицу для хранения названия страны. Тогда шаги будут следующими:

1) Создайте / запустите сценарий, который обходит столбцы в исходной таблице и создает запись в SpeciesFoundInCountry для каждого столбца, который имеет истинное значение. Эта запись будет содержать название страны. 2) Запустите оператор SQL, который обновляет поле SpeciesFoundInCountry.CountryID, присоединившись к таблице Страна в названии страны. 3) Очистите таблицу SpeciesFoundInCountry, удалив столбец CountryName.

Вот небольшой псевдокод MS Access VB / VBA, чтобы дать вам суть

Public Sub CreateRelationshipRecords()

  Dim rstSource as DAO.Recordset
  Dim rstDestination as DAO.Recordset
  Dim fld as DAO.Field
  dim strSQL as String
  Dim lngSpeciesID as Long

  strSQL = "SELECT * FROM [ORIGINALTABLE]"
  Set rstSource = CurrentDB.OpenRecordset(strSQL)
  set rstDestination = CurrentDB.OpenRecordset("SpeciesFoundInCountry")

  rstSource.MoveFirst

  ' Step through each record in the original table
  Do Until rstSource.EOF
    lngSpeciesID = rstSource.ID
    ' Now step through the fields(columns). If the field
    ' value is one (1), then create a relationship record
    ' using the field name as the Country Name
    For Each fld in rstSource.Fields
      If fld.Value = 1 then
        with rstDestination
          .AddNew
          .Fields("CountryID").Value = Null
          .Fields("CountryName").Value = fld.Name
          .Fields("SpeciesID").Value = lngSpeciesID
          .Update
        End With
      End IF
    Next fld  
    rstSource.MoveNext
  Loop

  ' Clean up
  rstSource.Close
  Set rstSource = nothing
  ....

End Sub

После этого вы можете запустить простую инструкцию SQL для обновления значений CountryID в таблице SpeciesFoundInCountry.

ОБНОВЛЕНИЕ SpeciesFoundInCountry ВНУТРЕННЕЕ СОЕДИНЕНИЕ Страна ON SpeciesFoundInCountry.CountryName = Country.CountryName SET SpeciesFoundInCountry.CountryID = Country.CountryID;

Наконец, все, что вам нужно сделать, это очистить таблицу SpeciesFoundInCountry, удалив столбец CountryName.

**** СТОРОННОЕ ПРИМЕЧАНИЕ: Я нашел полезным иметь таблицы стран, которые также включают сокращения ISO (коды стран). Иногда они используются в качестве внешних ключей в других таблицах, поэтому соединение с таблицей "Страна" не нужно включать в запросы.

Для получения дополнительной информации: http://en.wikipedia.org/wiki/Iso_country_codes

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...