как устранить дубликаты из моей функции в postgresql? - PullRequest
1 голос
/ 24 марта 2019

У меня есть таблица данных vehicle_data: CREATE TABLE public.vehicle_data

CREATE TABLE vehicle_data
(
  model_name text NOT NULL,
  record_date text NOT NULL,
  inv_quantity integer,
  CONSTRAINT vehicle_data_pkey PRIMARY KEY (model_name, record_date)
)

Мой стол выглядит так:

model_name  record_date
  car1         5-2015
  car1         1-2016
  car1         2-2015
  car2         2-2017
  car3         8-2016

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

model_name  record_date
  car1         2-2015
  car1         5-2015
  car1         1-2016

Поскольку моя record_date - TEXT, я подумал, что в своей функции я могу разделить массив TEXT, используя split_part (record_date, '-', 2), чтобы получить значение года, сохранить все уникальные значения в массиве, а затем запустить мой выберите запрос для каждого года.

CREATE OR REPLACE FUNCTION getdata(model text)
   RETURNS TABLE(a text, b text) AS
$BODY$
DECLARE  i int;
     list TEXT[]:= ARRAY(SELECT DISTINCT split_part(record_date,'-',2) as xyz
             from vehicle_data 
             order by xyz);
BEGIN
i:=0;
WHILE i < (select cardinality(list)-1) LOOP 
RETURN QUERY
    select model_name, record_date
    from vehicle_data
    where model_name LIKE model AND split_part(record_date,'-',2) LIKE list[i]
    order by length(record_date), record_date ASC;
i:=i+1;
END LOOP;
RETURN;
END;
$BODY$
  LANGUAGE plpgsql;

Хотя функция работает, она дублирует результаты 68 раз, а не останавливается.

1 Ответ

2 голосов
/ 24 марта 2019

Для решения вашего непосредственного вопроса: для итерации по массиву используйте цикл FOREACH, а не цикл WHILE. Поэтому вы должны изменить свою функцию на что-то вроде этого:

CREATE OR REPLACE FUNCTION getdata(p_model text)
   RETURNS TABLE(a text, b text) AS
$BODY$
DECLARE  
  l_year text;
  l_year_list TEXT[]:= ARRAY(SELECT DISTINCT split_part(record_date,'-',2) as xyz
                             from vehicle_data);
BEGIN
  foreach l_year in array l_year_list loop
    RETURN QUERY
      select model_name, record_date
      from vehicle_data
      where model_name LIKE p_model 
        AND split_part(record_date,'-',2) = l_year
      order by length(record_date), record_date ASC;
  END LOOP;
END;
$BODY$
  LANGUAGE plpgsql;

Поскольку подстановочные знаки не используются, я изменил LIKE на =. Я также применил другой шаблон именования к параметрам и переменным.

Но Postgres обладает действительно мощными возможностями массива в SQL, поэтому вышеприведенное можно переписать в один запрос без цикла:

CREATE OR REPLACE FUNCTION getdata(p_model text)
   RETURNS TABLE(a text, b text) AS
$BODY$
DECLARE  
  l_year_list TEXT[]:= ARRAY(SELECT DISTINCT split_part(record_date,'-',2) as xyz
                             from vehicle_data);
BEGIN
  RETURN QUERY
    select model_name, record_date
    from vehicle_data
    where model_name LIKE p_model 
      AND split_part(record_date,'-',2) = ANY(l_year_list)
    order by length(record_date), record_date ASC;
END;
$BODY$
  LANGUAGE plpgsql;

Это берет все лет из таблицы и возвращает те строки, в которых год в строке внутреннего запроса (только один в случае второго варианта) равен по крайней мере одному из значений в массив, который имеет значение true для всех строк, поскольку значения в массиве - это все существующих лет в этом столбце. Таким образом, по крайней мере одно из значений в массиве будет соответствовать значению в строке, просматриваемой в данный момент при обработке запроса, что, в свою очередь, означает, что в целом условие вообще не является необходимым.

Вы можете представить, что ваша функция (логически) обрабатывается так:

  1. получить все годы в массиве, поэтому массив содержит {2015, 2016, 2017}
  2. найти все строки, в которых совпадает название модели. Для вашего примера это оставляет нас с

    модель_имя запись_дата автомобиль1 5-2015 автомобиль1 1-2016 car1 2-2015

  3. Просмотрите перечисленные выше строки и посмотрите, совпадает ли годовая часть record_date с какой-либо из дат в массиве. Это условие всегда будет истинным, так как массив содержит все возможные значения для этого столбца. Таким образом, условие ничего не удаляет из результата.

Это, в свою очередь, означает, что ваш запрос эквивалентен:

select vd1.model_name, vd1.record_date
from vehicle_data vd1
where vd1.model_name LIKE = 'car1'
order by split_part(record_date, '-', 2)::int, 
         split_part(record_date, '-', 1)::int;
...