如何从我的 postgresql 函数中删除重复项?
how to eliminate duplicates from my function in postgresql?
我有这个 vehicle_data table: 创建 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)
)
我的 table 看起来像:
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,我认为在我的函数中我可以使用 split_part(record_date,'-',2) 拆分 TEXT 数组以获得年份值,将所有唯一值存储在一个数组中,然后 运行 我的 select 查询每年。
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 次,而不是停止。
解决您的直接问题:要遍历数组,请使用 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;
这需要 所有 年从 table 和 returns 那些行中的年份在内部 quer 的行中(只有一个在情况下第二个变体)至少等于数组中的一个值,这对所有行都是正确的,因为数组中的值是该列中 all 的现有年份。因此,在处理查询时,数组中的至少一个值将与当前正在查看的行中的值相匹配,这反过来意味着整个条件根本不是必需的。
你可以想象你的函数正在(逻辑上)被这样处理:
- 将所有年份放入一个数组中,因此该数组包含 {2015, 2016, 2017}
查找模型名称匹配的所有行。对于您的示例,这给我们留下了
model_name record_date
car1 5-2015
car1 1-2016
car1 2-2015
浏览上面的行,看看 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;
我有这个 vehicle_data table: 创建 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)
)
我的 table 看起来像:
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,我认为在我的函数中我可以使用 split_part(record_date,'-',2) 拆分 TEXT 数组以获得年份值,将所有唯一值存储在一个数组中,然后 运行 我的 select 查询每年。
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 次,而不是停止。
解决您的直接问题:要遍历数组,请使用 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;
这需要 所有 年从 table 和 returns 那些行中的年份在内部 quer 的行中(只有一个在情况下第二个变体)至少等于数组中的一个值,这对所有行都是正确的,因为数组中的值是该列中 all 的现有年份。因此,在处理查询时,数组中的至少一个值将与当前正在查看的行中的值相匹配,这反过来意味着整个条件根本不是必需的。
你可以想象你的函数正在(逻辑上)被这样处理:
- 将所有年份放入一个数组中,因此该数组包含 {2015, 2016, 2017}
查找模型名称匹配的所有行。对于您的示例,这给我们留下了
model_name record_date car1 5-2015 car1 1-2016 car1 2-2015
浏览上面的行,看看 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;