As lambda_record
function returns RECORD
and doesn't have OUT
parameter, you must define the columns and types it will return when calling using an alias:
SELECT r.out1, r.out2
FROM lambda_record(...) AS r(out1 numeric, out2 numeric);
In this case r
is the table alias, and out1
/out2
the alias for the columns with the types defined (defining types is only necessary when it returns RECORD
and doesn't have OUT
parameters).
EDIT: even though it uses a RECORD
, lambda_record
still calls the function as it had only one column returned, using SELECT func_name(params...)
method:
sql := format(
'select %s(%s)',
foid::oid::regproc,
array_to_string(call_args, ', ')
);
raise debug 'sql=%', sql;
You could change that line to call it use SELECT * FROM func_name(params...)
:
sql := format(
'select * from %s(%s)',
foid::oid::regproc,
array_to_string(call_args, ', ')
);
raise debug 'sql=%', sql;
That way you should already have it return the columns correctly (even for functions with a single column), and now you can call as I showed in the beginning.
- For the Nested Loop, there is nothing wrong with that.. You have less than five rows you're returning and you do it in one loop.
- I'm not sure what you mean as batch. You'll have to show your other queries. Why are you running a
UNION ALL
a bunch of times to begin with?
Try something like this for your batch,
SELECT
test_ipv6.ip,
test_ipv6.data,
test_cidr_ipv6.cidr,
test_cidr_ipv6.data,
test_ipv6.start,
test_ipv6.end,
test_cidr_ipv6.start,
test_cidr_ipv6.end
FROM test_ipv6
INNER JOIN test_cidr_ipv6
ON (test_ipv6.ip <<= cidr )
WHERE
test_ipv6.ip = any(ARRAY[ip6('2001:DB8::1'),ip6('2001:DB8::1')]::ip6[])
AND '2017-01-01 00:00:00.100000' BETWEEN test_ipv6.start AND test_ipv6.end
AND '2017-01-01 00:00:00.100000' BETWEEN test_cidr_ipv6.start AND test_cidr_ipv6.end;
You may have better luck with a CTE or inlining something like this...
WITH t AS (
SELECT ipv6(iptext) AS ip
FROM (VALUES
('fddf:c4c1:2573::/48') ,
('fd8d:d482:3c08::/48')
) AS v(iptext)
)
SELECT
test_ipv6.ip,
test_ipv6.data,
test_cidr_ipv6.cidr,
test_cidr_ipv6.data,
test_ipv6.start,
test_ipv6.end,
test_cidr_ipv6.start,
test_cidr_ipv6.end
FROM test_ipv6
INNER JOIN test_cidr_ipv6
ON (test_ipv6.ip <<= cidr)
INNER JOIN t
USING (ip)
WHERE
'2017-01-01 00:00:00.100000' BETWEEN test_ipv6.start AND test_ipv6.end
AND '2017-01-01 00:00:00.100000' BETWEEN test_cidr_ipv6.start AND test_cidr_ipv6.end;
Best Answer
1) Function
anyarray_uniq
can be simplified in several ways to make it faster (note that in the function's body the input parameter can be accessed not only by the name but also by the number:$<n>
):or yet simpler using pure SQL:
Second one is slower then first but still faster then the original on my tests.
Those functions doing exactly the same thing as
anyarray_uniq
(removes duplicates and keeps the order of the elements), but for your purpose the order is irrelevant, so the simplest way (using function) isand now because the elements order changed you should to compare the arrays length instead of its content:
2) To achieve your goal you are doing ambiguous work by calling the function (it is also slowing down the query performance), calculating the result as array without duplicates and finally comparing two arrays. The actual goal is to compare the whole array length against the count of the distinct values:
Upd:
3) When you fix your data using one of the functions above
you can avoid those situation by creating constraint on the field:
Actually you can use this function in the question's query:
4) Try to follow to the common database designing rules called "database normalization". The example you provided is exactly about the First and Second normal forms.
Let's imagine that you need the phone's additional info like "home/work/mobile", "internal code", "availability time" and so on. Using your current design it can be problematic.