выберите самую последнюю запись из денормализованной таблицы

#sql #oracle #select

#sql #Oracle #выберите

Вопрос:

У меня настроена таблица со столбцами:

  • fname
  • lname
  • адрес
  • город
  • состояние
  • zip
  • Идентификатор клиента
  • date_modified

данные в основном денормализованы, поэтому выглядят так

Бен -- Смит--*** 123first*** st -- Нью-Йорк -- Нью-Йорк -- 12101 -- 123 -- 1-1-2011
Бен -- Смит--*** 123 1-й ст *** -- Нью-Йорк -- Нью-Йорк -- 12101 -- 123 -- 1-1-2011
Сара - Смит - БЛА-БЛА-БЛА

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

Я попытался сделать что-то вроде

  insert into new_table(fname,lname,address,city,state,zip,Customer_ID,
                      date_modified)

 select fname,lname,address,city,state,zip,Customer_ID,date_modified
 group by (fname,lname,address,city,state,zip,Customer_ID,date_modified)
 

проблема в том, что слишком много адресов и других столбцов имеют одинаковое значение, но разный текст (первый против 1-го). таким образом, group by оставляет обе эти записи в новой таблице. как мне получить одну запись для каждого идентификатора клиента, выбрав максимальное значение (дата изменения). В принципе, я бы хотел группировать только по идентификатору customer_id, а не по остальным столбцам, но это не разрешено в oracle.

Комментарии:

1. Обратите внимание, что group by это не функция. Скобки совершенно бесполезны.

2. Вам будет лучше всего изменить дизайн вашей базы данных, с этим есть некоторые проблемы. В зависимости от необходимости: 1) Разделите адрес на его собственную таблицу, затем создайте таблицу перекрестных ссылок между адресом и клиентом. 2) Отделите имя от его собственной таблицы, затем таблицу перекрестных ссылок между именем и клиентом со столбцом для ‘part_of_name’ (т.Е. — family, given, nick и т. Д.).

Ответ №1:

Ниже приведен один из нескольких способов получить то, что вы, кажется, хотите. Имейте в виду, однако, что это не нормализует вашу базу данных. У вас все еще есть customer_id вход с именем, фамилией и адресом. Я бы, вероятно, превратил это в несколько вставок, одну, чтобы получить все уникальные идентификаторы клиентов вместе с последней информацией об именах для таблицы Customers, а затем другую вставку для адресов. Если вам нужна историческая информация об изменениях, вам нужно будет соответствующим образом настроить.

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

 INSERT INTO New_Table (
    fname,
    lname,
    address,
    city,
    state,
    zip,
    Customer_ID,
    date_modified )
SELECT
    OT1.fname,
    OT1.lname,
    OT1.address,
    OT1.city,
    OT1.state,
    OT1.zip,
    OT1.customer_id,
    OT1.date_modified
FROM
    (
    SELECT
        customer_id,
        MAX(date_modified) AS latest_date_modified
    FROM
        Old_Table
    GROUP BY customer_id) SQ
INNER JOIN Old_Table OT1 ON
    OT1.customer_id = OT1.customer_id AND
    OT1.date_modified = SQ.latest_date_modified
 

Ответ №2:

Это довольно просто, используя аналитические (или оконные) функции для выбора первой строки для каждого клиента. В случае, если две строки имеют одинаковую date_modified , не определено, какая из них берется.

 INSERT INTO new_table (fname,lname,address,city,state,zip,Customer_ID,date_modified)
SELECT fname,
       lname,
       address,
       city,
       state,
       zip,
       Customer_ID,
       date_modified
FROM (
   SELECT fname,
          lname,
          address,
          city,
          state,
          zip,
          Customer_ID,
          date_modified,
          row_number() over (partition by customer_id order by date_modified desc) as rn
) 
WHERE rn = 1