我想知道是否可以在不创建任何表和索引的情况下优化此功能。
这是功能:
CREATE OR REPLACE FUNCTION decode_trame_v3(IN tid integer)
RETURNS TABLE(id integer, card numeric, kilo double precision) AS
$BODY$
DECLARE
v_weights character varying;
v_waste_no integer = 0;
v_number_waste integer;
BEGIN
SELECT weights FROM data WHERE data.id = tid INTO v_weights;
SELECT COALESCE(length(v_weights)/7, 0) INTO v_number_waste;
LOOP
EXIT WHEN v_number_waste = v_waste_no;
RETURN query
WITH convert_table (letter, value) AS (
SELECT '-' AS letter, 63 AS value
UNION SELECT ':', 62
UNION SELECT 'z', 61
UNION SELECT 'y', 60
UNION SELECT 'x', 59
UNION SELECT 'w', 58
UNION SELECT 'v', 57
UNION SELECT 'u', 56
UNION SELECT 't', 55
UNION SELECT 's', 54
UNION SELECT 'r', 53
UNION SELECT 'q', 52
UNION SELECT 'p', 51
UNION SELECT 'o', 50
UNION SELECT 'n', 49
UNION SELECT 'm', 48
UNION SELECT 'l', 47
UNION SELECT 'k', 46
UNION SELECT 'j', 45
UNION SELECT 'i', 44
UNION SELECT 'h', 43
UNION SELECT 'g', 42
UNION SELECT 'f', 41
UNION SELECT 'e', 40
UNION SELECT 'd', 39
UNION SELECT 'c', 38
UNION SELECT 'b', 37
UNION SELECT 'a', 36
UNION SELECT 'Z', 35
UNION SELECT 'Y', 34
UNION SELECT 'X', 33
UNION SELECT 'W', 32
UNION SELECT 'V', 31
UNION SELECT 'U', 30
UNION SELECT 'T', 29
UNION SELECT 'S', 28
UNION SELECT 'R', 27
UNION SELECT 'Q', 26
UNION SELECT 'P', 25
UNION SELECT 'O', 24
UNION SELECT 'N', 23
UNION SELECT 'M', 22
UNION SELECT 'L', 21
UNION SELECT 'K', 20
UNION SELECT 'J', 19
UNION SELECT 'I', 18
UNION SELECT 'H', 17
UNION SELECT 'G', 16
UNION SELECT 'F', 15
UNION SELECT 'E', 14
UNION SELECT 'D', 13
UNION SELECT 'C', 12
UNION SELECT 'B', 11
UNION SELECT 'A', 10
UNION SELECT '9', 9
UNION SELECT '8', 8
UNION SELECT '7', 7
UNION SELECT '6', 6
UNION SELECT '5', 5
UNION SELECT '4', 4
UNION SELECT '3', 3
UNION SELECT '2', 2
UNION SELECT '1', 1
UNION SELECT '0', 0
)
SELECT tid AS id,
(((SELECT value FROM convert_table WHERE letter =substring(v_weights from 4+(v_waste_no*7) for 1)::character varying) % 8)*2 +
trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 5+(v_waste_no*7) for 1)::character varying)/32, 0)
) * 1 +
(((SELECT value FROM convert_table WHERE letter =substring(v_weights from 3+(v_waste_no*7) for 1)::character varying) % 2)*8 +
trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 4+(v_waste_no*7) for 1)::character varying) / 8, 0)
)*16 +
(trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 3+(v_waste_no*7) for 1)::character varying) / 2, 0) -
trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 3+(v_waste_no*7) for 1)::character varying) /32 ,0)*16
) * 256 +
(((SELECT value FROM convert_table WHERE letter =substring(v_weights from 2+(v_waste_no*7) for 1)::character varying) % 8 ) * 2 +
trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 3+(v_waste_no*7) for 1)::character varying) / 32, 0)
) * 4096 +
(trunc((SELECT value FROM convert_table WHERE letter =substring(v_weights from 2+(v_waste_no*7) for 1)::character varying) / 8, 0) +
((SELECT value FROM convert_table WHERE letter = substring(v_weights from 1+(v_waste_no*7) for 1)::character varying) % 2) * 8
) * 65536 +
(trunc((SELECT value FROM convert_table WHERE letter = substring(v_weights from 1+(v_waste_no*7) for 1)::character varying) / 2, 0)
) * 1048576
AS card,
(((SELECT value FROM convert_table WHERE letter =substring(v_weights from 6+(v_waste_no*7) for 1)::character varying) % 16) * 64 +
(SELECT value FROM convert_table WHERE letter =substring(v_weights from 7+(v_waste_no*7) for 1)::character varying)) * 50 /1000::real
AS kilo;
v_waste_no = v_waste_no + 1;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
当我使用此查询调用函数时,大约需要10秒钟:
SELECT (decode_trame_v3(id)).* FROM data
LIMIT 1000
然后我用这个新函数简化了代码:
CREATE OR REPLACE FUNCTION decode_trame_v3(IN tid integer)
RETURNS TABLE(id integer, card numeric, kilo double precision) AS
$BODY$
DECLARE
v_weights character varying;
v_waste_no integer = 0;
v_number_waste integer;
BEGIN
SELECT weights FROM data WHERE data.id = tid INTO v_weights;
SELECT COALESCE(length(v_weights)/7, 0) INTO v_number_waste;
LOOP
EXIT WHEN v_number_waste = v_waste_no;
RETURN query
WITH convert_table (letter, value) AS (
SELECT '-' AS letter, 63 AS value
UNION SELECT ':', 62
UNION SELECT 'z', 61
UNION SELECT 'y', 60
UNION SELECT 'x', 59
UNION SELECT 'w', 58
UNION SELECT 'v', 57
UNION SELECT 'u', 56
UNION SELECT 't', 55
UNION SELECT 's', 54
UNION SELECT 'r', 53
UNION SELECT 'q', 52
UNION SELECT 'p', 51
UNION SELECT 'o', 50
UNION SELECT 'n', 49
UNION SELECT 'm', 48
UNION SELECT 'l', 47
UNION SELECT 'k', 46
UNION SELECT 'j', 45
UNION SELECT 'i', 44
UNION SELECT 'h', 43
UNION SELECT 'g', 42
UNION SELECT 'f', 41
UNION SELECT 'e', 40
UNION SELECT 'd', 39
UNION SELECT 'c', 38
UNION SELECT 'b', 37
UNION SELECT 'a', 36
UNION SELECT 'Z', 35
UNION SELECT 'Y', 34
UNION SELECT 'X', 33
UNION SELECT 'W', 32
UNION SELECT 'V', 31
UNION SELECT 'U', 30
UNION SELECT 'T', 29
UNION SELECT 'S', 28
UNION SELECT 'R', 27
UNION SELECT 'Q', 26
UNION SELECT 'P', 25
UNION SELECT 'O', 24
UNION SELECT 'N', 23
UNION SELECT 'M', 22
UNION SELECT 'L', 21
UNION SELECT 'K', 20
UNION SELECT 'J', 19
UNION SELECT 'I', 18
UNION SELECT 'H', 17
UNION SELECT 'G', 16
UNION SELECT 'F', 15
UNION SELECT 'E', 14
UNION SELECT 'D', 13
UNION SELECT 'C', 12
UNION SELECT 'B', 11
UNION SELECT 'A', 10
UNION SELECT '9', 9
UNION SELECT '8', 8
UNION SELECT '7', 7
UNION SELECT '6', 6
UNION SELECT '5', 5
UNION SELECT '4', 4
UNION SELECT '3', 3
UNION SELECT '2', 2
UNION SELECT '1', 1
UNION SELECT '0', 0
),
separate_weights(coordonate, letter) AS (
SELECT 'K21', (SELECT value FROM convert_table WHERE letter = substring(v_weights from 1 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'L21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 2 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'M21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 3 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'N21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 4 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'O21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 5 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'P21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 6 +(v_waste_no*7) for 1)::character varying)
UNION SELECT 'Q21', (SELECT value FROM convert_table WHERE letter =substring(v_weights from 7 +(v_waste_no*7) for 1)::character varying)
),
calc_weights(coordonate, value) AS (
SELECT 'S21', trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'K21') / 2, 0)
UNION SELECT 'T21', trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'L21') / 8, 0) + (( SELECT letter FROM separate_weights WHERE coordonate = 'K21') % 2) * 8
UNION SELECT 'U21', (( SELECT letter FROM separate_weights WHERE coordonate = 'L21') % 8) * 2 + trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'M21') / 32, 0)
UNION SELECT 'V21', trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'M21') / 2, 0) - trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'M21') / 32, 0) * 16
UNION SELECT 'W21', (( SELECT letter FROM separate_weights WHERE coordonate = 'M21') % 2) * 8 + trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'N21') / 8, 0)
UNION SELECT 'X21', (( SELECT letter FROM separate_weights WHERE coordonate = 'N21') % 8) * 2 + trunc(( SELECT letter FROM separate_weights WHERE coordonate = 'O21') / 32, 0)
),
card(card) AS (
SELECT (
(SELECT value FROM calc_weights WHERE coordonate = 'X21') * 1 +
(SELECT value FROM calc_weights WHERE coordonate = 'W21') * 16 +
(SELECT value FROM calc_weights WHERE coordonate = 'V21') * 256 +
(SELECT value FROM calc_weights WHERE coordonate = 'U21') * 4096 +
(SELECT value FROM calc_weights WHERE coordonate = 'T21') * 65536 +
(SELECT value FROM calc_weights WHERE coordonate = 'S21') * 1048576
)
),
kilo(kilo) AS (
SELECT (
((( SELECT letter FROM separate_weights WHERE coordonate = 'P21') % 16) * 64 + ( SELECT letter FROM separate_weights WHERE coordonate = 'Q21')) * 50 /1000::double precision
)
)
SELECT tid AS id, (SELECT card.card FROM card) AS card, (SELECT kilo.kilo FROM kilo) AS kilo;
v_waste_no = v_waste_no + 1;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
我认为它会更快但这个新功能需要大约12-13秒(总是使用相同的查询)。
似乎“UNION SELECT”正在减慢查询速度。
如何在不创建任何表或索引的情况下加快查询速度?
非常感谢您的帮助!
编辑:
我发现我的问题太不精确和太大,所以我把问题简化为一个简单的问题:
我想知道是否可以在没有“UNION SELECT”的情况下创建虚拟表?
我不想创建表或临时表。
现在用于创建包含数据的虚拟表,我使用:
WITH convert_table (letter, value) AS (
SELECT '-' AS letter, 63 AS value
UNION SELECT ':', 62
UNION SELECT 'z', 61
UNION SELECT 'y', 60
UNION SELECT 'x', 59
UNION SELECT 'w', 58
UNION SELECT 'v', 57
UNION SELECT 'u', 56
UNION SELECT 't', 55
UNION SELECT 's', 54
UNION SELECT 'r', 53
UNION SELECT 'q', 52
UNION SELECT 'p', 51
...)
SELECT * FROM convert_table
我不觉得这很漂亮且易于使用。
还有其他方法可以完成同样的工作吗?
对不起来的新人抱歉!
答案 0 :(得分:2)
使用values
关键字:
WITH convert_table (letter, value) AS
(
values
('-', 63),
(':', 62),
('z', 61),
...
)
SELECT *
FROM convert_table;
您实际上不需要CTE,您可以直接使用VALUES
子句:
select *
from (
values
('-', 63),
(':', 62),
('z', 61)
) as convert_table (letter, value);
如果您在最终查询中多次使用值列表,CTE会更方便。
但这不会比使用UNION更快,只需减少打字。
答案 1 :(得分:1)
相当彻底的清理使它看起来像这样:
CREATE OR REPLACE FUNCTION decode_trame_v3(tid integer)
RETURNS TABLE(id integer, card numeric, kilo double precision) AS
$BODY$
DECLARE
v_weights character varying;
v_waste_no integer := 0;
v_number_waste integer;
cvt char[];
v1 int;
v2 int;
v3 int;
v4 int;
v5 int;
v6 int;
v7 int;
BEGIN
SELECT weights INTO v_weights FROM data WHERE data.id = tid;
v_number_waste := coalesce(length(v_weights)/7, 0) * 7;
cvt := '[0:63]={0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,'
'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,:,-}';
id := tid;
LOOP
EXIT WHEN v_waste_no = v_number_waste;
v1 := array_position(cvt, substring(v_weights from 1+v_waste for 1));
v2 := array_position(cvt, substring(v_weights from 2+v_waste for 1));
v3 := array_position(cvt, substring(v_weights from 3+v_waste for 1));
v4 := array_position(cvt, substring(v_weights from 4+v_waste for 1));
v5 := array_position(cvt, substring(v_weights from 5+v_waste for 1));
v6 := array_position(cvt, substring(v_weights from 6+v_waste for 1));
v7 := array_position(cvt, substring(v_weights from 7+v_waste for 1));
card := (v4 % 8) * 2 +
trunc(v5/32, 0) * 1 +
(v5 % 2) * 8 +
trunc(v4 / 8, 0) * 16 +
trunc(v3 / 2, 0) -
trunc(v3 /32 ,0) * 16 * 256 +
(v2 % 8 ) * 2 +
trunc(v3 / 32, 0) * 4096 +
trunc(v2 / 8, 0) +
(v1 % 2) * 8 * 65536 +
trunc(v1 / 2, 0) * 1048576;
kilo := (v6 % 16) * 64 + v7 * 50 / 1000.;
RETURN NEXT;
v_waste_no = v_waste_no + 7;
END LOOP;
RETURN;
END;
$BODY$ LANGUAGE plpgsql VOLATILE COST 100 ROWS 1000;
首先,你的整个CTE已经变成了一个单独的数组,并且它是在循环之外构造的,因此只创建一次。在循环内部,您可以找到"值",现在是array_position()
的数组索引。因为你有多个具有相同参数的调用,所以我创建了7个变量v1..v7
来保存数组索引。 SELECT
已被简单赋值所取代,然后循环变得非常简单和可读。变量v_waste_number
增加7,因此您可以忘记所有乘法。
请注意,array_position()
是PG 9.5中的新功能。如果您使用的是旧版本,则应使用ctv char(64) := '0123...'
,然后使用:
v1 := position(substring(v_weights from 1+v_waste for 1) in cvt) - 1;
...
这可能会慢一些,但很难说多少。请注意最后的- 1
以使结果从0开始。
现在你可以实际看到""再次使用你的算法,你可以检查它,因为我可能已经删除了一些太多的括号(虽然它不认为我做了)。