Pass dynamic parameters into a Table Valued Function [duplicate] - sql

I have a table-value function that takes an ID number of a person and returns a few rows and columns. In another query, I am creating a SELECT that retrieves a lot of information about many people. How can I pass an id number from my main query to my function to sum a column and join it to my main query? I wish I didn't have a table value function since that would work easily, however, this function is used elsewhere and I'd like to reuse it. Perhaps this isn't even possible with a table-value function and I need to create a scalar one.
My main Query looks like this:
select id_num, name, balance
from listOfPeople
And the table-value function looks like this:
calculatePersonalDiscount(id_number)
I would like to do something like:
select id_num, name, balance
from listOfPeople
left join
(
SELECT id_num, SUM(discount)
FROM calculatePersonalDiscount(listOfPeople.id_num)
) x ON x.id_num = listOfPeople.id_num
But you can't pass listOfPeople.id_num into the function since it's not really the same scope.

In SQL Server 2005 you can use the CROSS APPLY syntax:
select id_num, name, balance, SUM(x.discount)
from listOfPeople
cross apply dbo.calculatePersonalDiscount(listOfPeople.id_num) x
Likewise there's an OUTER APPLY syntax for the equivalent of a LEFT OUTER join.

I needed this badly and you gave me the start, and here I was able to JOIN on another View. (another table would work too), and I included another function in the WHERE clause.
SELECT CL_ID, CL_LastName, x.USE_Inits
FROM [dbo].[tblClient]
JOIN dbo.vw_InsuranceAppAndProviders ON dbo.vw_InsuranceAppAndProviders.IAS_CL_ID = tblClient.CL_ID
CROSS APPLY dbo.ufx_HELPER_Client_CaseWorker(tblClient.CL_ID) x
WHERE dbo.vw_InsuranceAppAndProviders.IAS_CL_ID = tblClient.CL_ID
AND dbo.ufx_Client_IsActive_By_CLID(tblClient.CL_ID) = 1
AND (dbo.vw_InsuranceAppAndProviders.IAS_InsuranceType = 'Medicare')

Related

how to use listagg operator so that the query should fetch comma separated values

SELECT (SELECT STRING_VALUE
FROM EMP_NODE_PROPERTIES
WHERE NODE_ID=AN.ID ) containedWithin
FROM EMP_NODE AN
WHERE AN.STORE_ID = ALS.ID
AND an.TYPE_QNAME_ID=(SELECT ID
FROM EMP_QNAME
where LOCAL_NAME = 'document')
AND
AND AN.UUID='13456677';
from the above query I am getting below error.
ORA-01427: single-row subquery returns more than one row
so how to change the above query so that it should fetch comma separated values
This query won't return error you mentioned because
there are two ANDs and
there's no ALS table (or its alias).
I suggest you post something that is correctly written, then we can discuss other errors.
Basically, it is either select string_value ... or select id ... (or even both of them) that return more than a single value.
The most obvious "solution" is to use select DISTINCT
another one is to include where rownum = 1
or, use aggregate functions, e.g. select max(string_value) ...
while the most appropriate option would be to join all tables involved and decide which row (value) is correct and adjust query (i.e. its WHERE clause) to make sure that desired value will be returned.
You would seem to want something like this:
SELECT LISTAGG(NP.STRING_VALUE, ',') WITHIN GROUP(ORDER BY NP.STRING_VALUE)
as containedWithin
FROM EMP_NODE N
JOIN EMP_QNAME Q
ON N.TYPE_QNAME_ID = Q.ID
LEFT JOIN EMP_NODE_PROPERTIES NP
ON NP.NODE_ID = N.ID
WHERE Q.LOCAL_NAME = 'document'
AND AN.UUID = '13456677';
This is a bit speculative because your original query would not run for the reason explained by Littlefoot.

Trying to replace a Cross Apply with a JOIN

We have a table, ProductHierarchy, which is set. The # of rows will not change. It has simple parent/child Product Hierarchy data in it.
We also have a Table-valued function which takes a ProductHierarchyId and then returns all rows where IsDescendantOf is true for that Id. Or in other words, it returns that row, plus all of its ancestors.
The problem is that with this Table-valued function, we have to use it with CROSS APPLY, and this is seriously slowing down the query.
My thought is to create either a second permanent table (or a temp table/table variable in the query in question) that has all possible results from the Table-valued function already in it. And then JOIN to that table instead of using the CROSS APPLY. In this table, we would add a column called something like QueryID. So instead of
CROSS APPLY dbo.GetProductLevels(P.ProductHierarchyId)
We could use
LEFT JOIN FutureTable ft ON ft.QueryId = P.ProductHierarchyId
I'm struggling with the query to create that table. Here's what I have so far...
SELECT
*
, 1 AS QueryId
FROM
dbo.ProductHierarchy
WHERE
(SELECT ProductHierarchyNode FROM dbo.ProductHierarchy WHERE ProductHierarchyId = 1).IsDescendantOf(ProductHierarchyNode) = 1
Ok, so that works great for the record where ProductHierarchyId = 1. But then I'd need to repeat that for ProductHierarchyId = 2:
SELECT
*
, 2 AS QueryId
FROM
dbo.ProductHierarchy
WHERE
(SELECT ProductHierarchyNode FROM dbo.ProductHierarchy WHERE ProductHierarchyId = 2).IsDescendantOf(ProductHierarchyNode) = 1
And then for 3, and then for 4, all the way to the last Id, doing a UNION each time, inside a loop -- which is hideous.
I KNOW there is a way to do this all in one query. Something like a recursive CTE. But my brain isn't getting there.
Wouldn't you just do this?
SELECT . . . -- the columns you want
INTO . . . -- where you want them
FROM dbo.ProductHierarchy ph CROSS APPLY
dbo.GetProductLevels(P.ProductHierarchyId);

large group of variable IDs

I have a working query that contains a large number of variable IDs. Rather than copying and pasting in each ID whenever I need to run a new query, I was wondering if there was a way to create a stored procedure out of the query below and pass in a group of IDs?
Here is the query. The IDs change all the time, so I'm trying to figure out a way of doing this easier but I'm not having much luck.
I thought about using a cursor in a stored procedure and just passing each ID, but that seems cumbersome and inefficient.
SELECT gm.geoId, T.number As surveyID, 0 as SpeciesCount
FROM (
VALUES (1994328036),(1994328037),(1994328038),(1994328039),(1994328040),(1994328041),(1994328042),(1994328043),
(1994328044),(1994328045),(1994328046),(1994328047),(1994328048),(1994328049),(1994328050),(1994328051),
(1994328052),(1994328053),(1994328054),(1994328055)
) AS T(number)
CROSS JOIN dbo.groupBiology gm
You can create a table-valued function (TVF) like this:
CREATE FUNCTION tvf_GetIDs ()
RETURNS
#output TABLE ( data int )
AS
BEGIN
INSERT INTO #output (data) VALUES
(1994328036),(1994328037),(1994328038),(1994328039),
(1994328040),(1994328041),(1994328042),(1994328043),
(1994328044),(1994328045),(1994328046),(1994328047),
(1994328048),(1994328049),(1994328050),(1994328051),
(1994328052),(1994328053),(1994328054),(1994328055)
RETURN
END
GO
then use this function wherever the IDs are required, e.g.
SELECT *
FROM Customers AS c
INNER JOIN (SELECT * FROM tvf_GetIDs()) t ON c.CustID = t.data
You only need to update the TVF whenever the IDs change.

Do something with each row and then get union of results - SQL (DB2)

I couldn't find solution for this problem.
I have table A with primary key ROW_ID, table B with same extern key and column SOMETHING.
Also, I have function created like this:
CREATE FUNCTION FIND_SOMETHING_FOR_ID(ROW_ID INTEGER)
RETURNS TABLE(SOMETHING INTEGER)
BEGIN ATOMIC
RETURN
SELECT SOME_SCALAR_FUNCTION(SOMETHING)
FROM B b
WHERE b.ROW_ID=ROW_ID;
END#
The thing I want to do is: for each ROW_ID in A get table returned by FIND_SOMETHING_FOR_ID
and then get UNION of all tables.
According to the documentation, you can do what you want as:
select fsfi.*
from A a cross join
table(find_something_for_id(a.row_id)) fsfi;
That is, a table-valued function can reference tables before it in the from clause, but not after it. (Note: I replaced the , in the from with cross join because I abhor commas in the from clause.)
By the way, SQL Server solves this problem with the cross apply operator.

Postgres analogue to CROSS APPLY in SQL Server

I need to migrate SQL queries written for MS SQL Server 2005 to Postgres 9.1.
What is the best way to substitute for CROSS APPLY in this query?
SELECT *
FROM V_CitizenVersions
CROSS APPLY
dbo.GetCitizenRecModified(Citizen, LastName, FirstName, MiddleName,
BirthYear, BirthMonth, BirthDay, ..... ) -- lots of params
GetCitizenRecModified() function is a table valued function. I can't place code of this function because it's really enormous, it makes some difficult computations and I can't abandon it.
In Postgres 9.3 or later use a LATERAL join:
SELECT v.col_a, v.col_b, f.* -- no parentheses, f is a table alias
FROM v_citizenversions v
LEFT JOIN LATERAL f_citizen_rec_modified(v.col1, v.col2) f ON true
WHERE f.col_c = _col_c;
Why LEFT JOIN LATERAL ... ON true?
Record returned from function has columns concatenated
For older versions, there is a very simple way to accomplish what I think you are trying to with a set-returning function (RETURNS TABLE or RETURNS SETOF record OR RETURNS record):
SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM v_citizenversions v
The function computes values once for every row of the outer query. If the function returns multiple rows, resulting rows are multiplied accordingly. All parentheses are syntactically required to decompose a row type. The table function could look something like this:
CREATE OR REPLACE FUNCTION f_citizen_rec_modified(_col1 int, _col2 text)
RETURNS TABLE(col_c integer, col_d text)
LANGUAGE sql AS
$func$
SELECT s.col_c, s.col_d
FROM some_tbl s
WHERE s.col_a = $1
AND s.col_b = $2
$func$;
You need to wrap this in a subquery or CTE if you want to apply a WHERE clause because the columns are not visible on the same level. (And it's better for performance anyway, because you prevent repeated evaluation for every output column of the function):
SELECT col_a, col_b, (f_row).*
FROM (
SELECT col_a, col_b, f_citizen_rec_modified(col1, col2) AS f_row
FROM v_citizenversions v
) x
WHERE (f_row).col_c = _col_c;
There are several other ways to do this or something similar. It all depends on what you want exactly.
Necromancing:
New in PostgreSQL 9.3:
The LATERAL keyword
left | right | inner JOIN LATERAL
INNER JOIN LATERAL is the same as CROSS APPLY
and LEFT JOIN LATERAL is the same as OUTER APPLY
Example usage:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
I like Erwin Brandstetter's answer however, I've discovered a performance problem:
when running
SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM v_citizenversions v
The f_citizen_rec_modified function will be ran 1 time for every column it returns (multiplied by every row in v_citizenversions). I did not find documentation for this effect, but was able to deduce it by debugging. Now the question becomes, how can we get this effect (prior to 9.3 where lateral joins are available) without this performance robbing side effect?
Update: I seem to have found an answer. Rewrite the query as follows:
select x.col1, x.col2, x.col3, (x.func).*
FROM (select SELECT v.col1, v.col2, v.col3, f_citizen_rec_modified(col1, col2) func
FROM v_citizenversions v) x
The key difference being getting the raw function results first (inner subquery) then wrapping that in another select that busts those results out into the columns. This was tested on PG 9.2
This link appears to show how to do it in Postgres 9.0+:
PostgreSQL: parameterizing a recursive CTE
It's further down the page in the section titled "Emulating CROSS APPLY with set-returning functions". Please be sure to note the list of limitations after the example.