SQL Server Computed Column with recursion - sql-server

MS SQL Server 2014
Table schema (part of one) like following:
where Coinh1d_Close has type float.
For analytic purposes i need another column, more precisely computed column, Coinh1d_EMA12 based on Coinh1d_Close and previous value itself. First value is always known and calculates based on Coinh1d_Close only. The following values must to be calculated based on both Coinh1d_Close and previous value of the same column Coinh1d_EMA12, like in Excel:
From Calculating value using previous value of a row in T-SQL i wrote some T-SQL expression
;with cteCalculation as (
select t.Coinh1d_Id, t.Coinh1d_Time, t.Coinh1d_Close, t.Coinh1d_Close as Column2
from Coinsh1d t
where t.Coinh1d_Id in (1)
union all
select t.Coinh1d_Id, t.Coinh1d_Time, t.Coinh1d_Close, cast(t.Coinh1d_Close as float)*cast(2 as float)/(cast(12 as float)+cast(1 as float))+cast(c.Column2 as float)*(cast(1 as float)-cast(2 as float)/(cast(12 as float)+cast(1 as float))) as Column2
from Coinsh1d t
inner join cteCalculation c
on t.Coinh1d_Id-1 = c.Coinh1d_Id
where t.Coinh1d_Name='BTC'
) select c.Coinh1d_Id, c.Coinh1d_Time, c.Coinh1d_Close, c.Column2
from cteCalculation c option (maxrecursion 0)
It gives exactly what i need
But my question is it possible to use previous value of Computed Column for the next value (i am using UDF function for Computed Column). I need like from this question Calculating value using previous value of a row in T-SQL but for Computed Column.
UPDATED
More information for clarification: this table consist of data for 1000 coins (BTC, ETH, etc.) and information for every coin starts with specific time Coinh1d_Time (Unix Timestamp) = 1346976000. At the begin we load to this table all info from this time to current time (about 2 000 000 records) and then every hour t-sql script updates this table (add 1000 row - new data per hour).
Also this table has many computed column (including one thet depends from Coinh1d_EMA12).
If it does not succeed to create Computed Column for Coinh1d_EMA12 i see solution: first of all create ordynary column Coinh1d_EMA12. Then one time update all table
ALTER PROCEDURE [dbo].[UpdateEMA12h1d_notused]
-- Add the parameters for the stored procedure here
#Coin varchar(50)
AS
BEGIN
SET NOCOUNT ON;
print #coin
;with cte as (
select
t.Coinh1d_Id, t.Coinh1d_Close, t.Coinh1d_Close as Column2
from
Coinsh1d t
where
t.Coinh1d_Id in (select MIN(Coinh1d_Id) from Coinsh1d where Coinh1d_Name=#Coin)
union all
select
t.Coinh1d_Id, t.Coinh1d_Close, cast(t.Coinh1d_Close as float)*cast(2 as float)/(cast(12 as float)+cast(1 as float))+cast(c.Column2 as float)*(cast(1 as float)-cast(2 as float)/(cast(12 as float)+cast(1 as float))) as Column2
from
Coinsh1d t
inner join
cte c
on
t.Coinh1d_Id-1 = c.Coinh1d_Id
where
t.Coinh1d_Name=#Coin
)
select
c.Coinh1d_Id, c.Column2
into
#tempEMA12
from
cte c
option
(maxrecursion 0)
update
t1
set
t1.Coinh1d_Ema12 = t2.Column2
from
Coinsh1d as t1
inner join
#tempEMA12 as t2
on
t1.Coinh1d_Id = t2.Coinh1d_Id
drop table #tempEMA12
END
Call for every coin:
DECLARE #MyCursor CURSOR;
DECLARE #MyField varchar(50);
BEGIN
SET #MyCursor = CURSOR FOR
select Coin_Symbol from Coins
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyField
WHILE ##FETCH_STATUS = 0
BEGIN
exec UpdateEMA12h1d_notused #MyField
FETCH NEXT FROM #MyCursor INTO #MyField
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;
It takes about 30 minutes. And then create trigger for this column with above CTE code.

Starting with the data from your function, you can use the LAG() (if on SQL Server 2012 or higher) window function to place a previous value in the same row. The code would follow this schema:
SELECT
C.CoinID,
C.CoinTime,
C.CoinClose,
C.FunctionComputedColumn,
PreviousRowFunctionComputedColumn =
LAG(
C.FunctionComputedColumn, -- The column you need from the previous row
1, -- How many rows back you need to go
0) -- Which value should it take if there is no previous row
OVER (
PARTITION BY
CoinID -- Row ordering resets with each different CoinID
ORDER BY
CoinTime ASC) -- Since it's ascending, the previous one is older
FROM
CoinData AS C
ORDER BY
C.CoinID,
C.CoinTIme
You can then use the PreviousRowFunctionComputedColumn in any expression you need.

Related

Can I use a variable inside cursor declaration?

Is this code valid?
-- Zadavatel Login ID
DECLARE #ZadavatelLoginId nvarchar(max) =
(SELECT TOP 1 LoginId
FROM
(SELECT Z.LoginId, z.Prijmeni, k.spojeni
FROM TabCisZam Z
LEFT JOIN TabKontakty K ON Z.ID = K.IDCisZam
WHERE druh IN (6,10)) t1
LEFT JOIN
(SELECT ko.Prijmeni, k.spojeni, ko.Cislo
FROM TabCisKOs KO
LEFT JOIN TabKontakty K ON K.IDCisKOs = KO.id
WHERE druh IN (6, 10)) t2 ON t1.spojeni = t2.spojeni
AND t1.Prijmeni = t2.Prijmeni
WHERE
t2.Cislo = (SELECT CisloKontOsoba
FROM TabKontaktJednani
WHERE id = #IdKJ))
-- Pokud je řešitelský tým prázdný
IF NOT EXISTS (SELECT * FROM TabKJUcastZam WHERE IDKJ = #IdKJ)
BEGIN
DECLARE ac_loginy CURSOR FAST_FORWARD LOCAL FOR
-- Zadavatel
SELECT #ZadavatelLoginId
END
ELSE BEGIN
I am trying to pass the variable #ZadavatelLoginId into the cursor declaration and SSMS keeps telling me there is a problem with the code even though it is working.
Msg 116, Level 16, State 1, Procedure et_TabKontaktJednani_ANAFRA_Tis_Notifikace, Line 575 [Batch Start Line 7]
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Can anyone help?
I do not see anything in your posted query that could trigger the specific message that you listed. You might get an error if the subquery (SELECT CisloKontOsoba FROM TabKontaktJednani WHERE id = #IdKJ) returned more than one value, but that error would be a very specific "Subquery returned more than 1 value...".
However, as written, your cursor query is a single select of a scalar, which would never yield anything other than a single row.
If you need to iterate over multiple user IDs, but wish to separate your selection query from your cursor definition, what you likely need is a table variable than can hold multiple user IDs instead of a scalar variable.
Something like:
DECLARE #ZadavatelLoginIds TABLE (LoginId nvarchar(max))
INSERT #ZadavatelLoginIds
SELECT t1.LoginId
FROM ...
DECLARE ac_loginy CURSOR FAST_FORWARD LOCAL FOR
SELECT LoginId
FROM #ZadavatelLoginIds
OPEN ac_loginy
DECLARE #LoginId nvarchar(max)
FETCH NEXT FROM ac_loginy INTO #LoginId
WHILE ##FETCH_STATUS = 0
BEGIN
... Send email to #LoginId ...
FETCH NEXT FROM ac_loginy INTO #LoginId
END
CLOSE ac_loginy
DEALLOCATE ac_loginy
A #Temp table can also be used in place of the table variable with the same results, but the table variable is often more convenient to use.
As others have mentioned, I believe that your login selection query is overly complex. Although this was not the focus of your question, I still suggest that you attempt to simplify it.
An alternative might be something like:
SELECT Z.LoginId
FROM TabKontaktJednani KJ
JOIN TabCisKOs KO ON KO.Cislo = KJ.CisloKontOsoba
JOIN TabCisZam Z ON Z.Prijmeni = KO.Prijmeni
JOIN TabKontakty K ON K.IDCisZam = Z.ID
WHERE KJ.id = #IdKJ
AND K.druh IN (6,10)
The above is my attempt to rewrite your posted query after tracing the relationships. I did not see any LEFT JOINS that were not superseded by other conditions that forced them into effectively being inner joins, so the above uses inner joins for everything. I have assumed that the druh column is in the TabKontakty table. Otherwise I see no need for that table. I do not guarantee that my re-interpretation is correct though.
How about you create a #temp table for each sub query since the problem is coming up due to the sub queries?
CREATE TABLE #TEMP1
(
LoginID nvarchar(max)
)
CREATE TABLE #TEMP2
(
ko.Prijmeni nvarchar(max),
k.spojeni nvarchar(max),
ko.Cislo nvarchar(max)
)

SQL - Add new column with outputs as values

Just wondering how I might go about adding the ouputted results as a new column to an exsisting table.
What I'm tryng to do is extract the date from a string which is in another column. I have the below code to do this:
Code
CREATE FUNCTION dbo.udf_GetNumeric
(
#strAlphaNumeric VARCHAR(256)
)
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^0-9]%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^0-9]%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
GO
Now use the function as
SELECT dbo.udf_GetNumeric(column_name)
from table_name
The issue is that I want the result to be placed in a new column in an exsisting table. I have tried the below code but no luck.
ALTER TABLE [Data_Cube_Data].[dbo].[DB_Test]
ADD reportDated nvarchar NULL;
insert into [DB].[dbo].[DB_Test](reportDate)
SELECT
(SELECT dbo.udf_GetNumeric(FileNamewithDate) from [DB].[dbo].[DB_Test])
The syntax should be an UPDATE, not an INSERT, because you want to update existing rows, not insert new ones:
UPDATE Data_Cube_Data.dbo.DB_Test -- you don't need square bracket noise
SET reportDate = dbo.udf_GetNumeric(FileNamewithDate);
But yeah, I agree with the others, the function looks like the result of a "how can I make this object the least efficient thing in my entire database?" contest. Here's a better alternative:
-- better, set-based TVF with no while loop
CREATE FUNCTION dbo.tvf_GetNumeric
(#strAlphaNumeric varchar(256))
RETURNS TABLE
AS
RETURN
(
WITH cte(n) AS
(
SELECT TOP (256) n = ROW_NUMBER() OVER (ORDER BY ##SPID)
FROM sys.all_objects
)
SELECT output = COALESCE(STRING_AGG(
SUBSTRING(#strAlphaNumeric, n, 1), '')
WITHIN GROUP (ORDER BY n), '')
FROM cte
WHERE SUBSTRING(#strAlphaNumeric, n, 1) LIKE '%[0-9]%'
);
Then the query is:
UPDATE t
SET t.reportDate = tvf.output
FROM dbo.DB_Test AS t
CROSS APPLY dbo.tvf_GetNumeric(t.FileNamewithDate) AS tvf;
Example db<>fiddle that shows this has the same behavior as your existing function.
The function
As i mentioned in the comments, I would strongly suggest rewriting the function, it'll perform terribly. Multi-line table value function can perform poorly, and you also have a WHILE which will perform awfully. SQL is a set based language, and so you should be using set based methods.
There are a couple of alternatives though:
Inlinable Scalar Function
SQL Server 2019 can inline function, so you could inline the above. I do, however, assume that your value can only contain the characters A-z and 0-9. if it can contain other characters, such as periods (.), commas (,), quotes (") or even white space ( ), or your not on 2019 then don't use this:
CREATE OR ALTER FUNCTION dbo.udf_GetNumeric (#strAlphaNumeric varchar(256))
RETURNS varchar(256) AS
BEGIN
RETURN TRY_CONVERT(int,REPLACE(TRANSLATE(LOWER(#strAlphaNumeric),'abcdefghigclmnopqrstuvwxyz',REPLICATE('|',26)),'|',''));
END;
GO
SELECT dbo.udf_GetNumeric('abs132hjsdf');
The LOWER is there in case you are using a case sensitive collation.
Inline Table Value Function
This is the better solution in my mind, and doesn't have the caveats of the above.
It uses a Tally to split the data into individual characters, and then only reaggregate the characters that are a digit. Note that I assume you are using SQL Server 2017+ here:
DROP FUNCTION udf_GetNumeric; --Need to drop as it's a scalar function at the moment
GO
CREATE OR ALTER FUNCTION dbo.udf_GetNumeric (#strAlphaNumeric varchar(256))
RETURNS table AS
RETURN
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL)) N(N)),
Tally AS(
SELECT TOP (LEN(#strAlphaNumeric))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4)
SELECT STRING_AGG(CASE WHEN V.C LIKE '[0-9]' THEN V.C END,'') WITHIN GROUP (ORDER BY T.I) AS strNumeric
FROM Tally T
CROSS APPLY (VALUES(SUBSTRING(#strAlphaNumeric,T.I,1)))V(C);
GO
SELECT *
FROM dbo.udf_GetNumeric('abs132hjsdf');
Your table
You define reportDated as nvarchar; this means nvarchar(1). Your function, however, returns a varchar(256); this will rarely fit in an nvarchar(1).
Define the column properly:
ALTER TABLE [dbo].[DB_Test] ADD reportDated varchar(256) NULL;
If you've already created the column then do the following:
ALTER TABLE [dbo].[DB_Test] ALTER COLUMN reportDated varchar(256) NULL;
I note, however, that the column is called "dated", which implies a date value, but it's a (n)varchar; that sounds like a flaw.
Updating the column
Use an UPDATE statement. Depending on the solution this would one of the following:
--Scalar function
UPDATE [dbo].[DB_Test]
SET reportDated = dbo.udf_GetNumeric(FileNamewithDate);
--Table Value Function
UPDATE DBT
SET reportDated = GN.strNumeric
FROM [dbo].[DB_Test] DBT
CROSS APPLY dbo.udf_GetNumeric(FileNamewithDate);

How do I use ##RowCount in a stored procedure, against rows in another table to work out the percentage?

Firstly, may I state that I'm aware of the ability to, e.g., create a new function, declare variables for rowcount1 and rowcount2, run a stored procedure that returns a subset of rows from a table, then determine the entire rowcount for that same table, assign it to the second variable and then 1 / 2 x 100....
However, is there a cleaner way to do this which doesn't result in numerous running of things like this stored procedure? Something like
select (count(*stored procedure name*) / select count(*) from table) x 100) as Percentage...
Sorry for the crap scenario!
EDIT: Someone has asked for more details. Ultimately, and to cut a very long story short, I wish to know what people would consider the quickest and most processor-concise method there would be to show the percentage of rows that are returned in the stored procedure, from ALL rows available in that table. Does that make more sense?
The code in the stored procedure is below:
SET #SQL = 'SELECT COUNT (DISTINCT c.ElementLabel), r.FirstName, r.LastName, c.LastReview,
CASE
WHEN c.LastReview < DateAdd(month, -1, GetDate()) THEN ''OUT of Date''
WHEN c.LastReview >= DateAdd(month, -1, GetDate()) THEN ''In Date''
WHEN c.LastReview is NULL THEN ''Not Yet Reviewed'' END as [Update Status]
FROM [Residents-'+#home_name+'] r
LEFT JOIN [CarePlans-'+#home_name+'] c ON r.PersonID = c.PersonID
WHERE r.Location = '''+#home_name+'''
AND CarePlanType = 0
GROUP BY r.LastName, r.FirstName, c.LastReview
HAVING COUNT(ELEMENTLABEL) >= 14
Thanks
Ant
I could not tell from your question if you are attempting to get the count and the result set in one query. If it is ok to execute the SP and separately calculate a table count then you could store the results of the stored procedure into a temp table.
CREATE TABLE #Results(ID INT, Value INT)
INSERT #Results EXEC myStoreProc #Parameter1, #Parameter2
SELECT
Result = ((SELECT COUNT(*) FROM #Results) / (select count(*) from table))* 100

Incrementing RECORD_ID When insterting records using a trigger

I am using a trigger to insert rows into a table using INSERT statement as below but when doing this the RECORD_ID number increments by 1 digit so all the records inserted have the same number..
This is what i'm using to increment the records from the trigger.
, ISNULL((
SELECT MAX([PROGRESS-RECID]) FROM [DBAdmin].[dbo].[ReTncyTransStatement]
),0) + 1 AS [PROGRESS-RECID]
This is what i'm using to load the data
;WITH TestTrans (
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE]
)
AS (SELECT
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE] from [SQLViewsPro2Live].[dbo].[RE-TNCY-TRANS] where [TRANS-DATE] between '2019-05-16 00:00:00.000' and '2019-05-17 00:00:00.000'
)
INSERT INTO [SQLViewsPro2Test].[dbo].[RE-TNCY-TRANS]
SELECT
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE]
FROM TestTrans;
GO
Any fixes appreciated
Thanks,
Full description of problem available here: T-SQL : create trigger to copy new columns from one table to another and increment no
Make PROGRESS-RECID an IDENTITY column and it will auto-increment.
Based on the linked question, you can rewrite your trigger as following:
CREATE TRIGGER AddReTncyTransStatement
ON [SQLViewsPro2EOD].[dbo].[RE-TNCY-TRANS]
AFTER UPDATE, INSERT
AS
BEGIN
DECLARE #ORG_CODE INT,
#TNCY_SYS_REF INT,
#TRANS_NO INT;
DECLARE C CURSOR FAST_FORWARD FOR(
SELECT Inserted.[ORG-CODE],
Inserted.[TNCY-SYS-REF],
Inserted.[TRANS-NO]
FROM Inserted);
OPEN C;
FETCH NEXT FROM C
INTO #ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO;
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [DBAdmin].[dbo].[ReTncyTransStatement]
(
[ORG-CODE],
[TNCY-SYS-REF],
[TRANS-NO],
[PROGRESS-RECID]
)
SELECT
#ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO,
ISNULL((SELECT MAX([PROGRESS-RECID]) FROM [DBAdmin].[dbo].[ReTncyTransStatement]),0) + 1 AS RECID;
FETCH NEXT FROM C
INTO #ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO
END;
CLOSE C;
DEALLOCATE C;
END;
Root of your problem:
When you use INSERT INTO ... SELECT(The one outside the trigger), trigger will be called once and the inserted table will contain all the records to be inserted. so the query inside the trigger will be run once, furthermore the SELECT MAX([PROGRESS-RECID]) will be calculated once. This means that if the inserted table contains 10 records, that are being inserted, then MAX(...) will be same for all of them!
How I Solved it:
Inside the trigger I used Cursor to iterate through the all records that are being inserted(For example 10 records), then in each iteration I insert one record to ReTncyTransStatement so the MAX(...) will be calculated and executed as expected.

SQL using a function in a trigger

I am creating a a trigger in SQL that will insert into another table after Insert on it. However I need to fetch a Value from the table to increment to be used in the insert.
I have a AirVisionSiteLog table. On insert on the table I would like for it to insert into another SiteLog table. However in order to do this I need to fetch the last Entry Number of the Site from the SiteLog table. Then on its insert take that result and increase by one for the new Entry Number. I am new to Triggers and Functions so I am not sure how to use them correctly. I believe I have a function to retrieve and increment the Entry Number however I am not sure how to use it in the Trigger.
My Function -
CREATE FUNCTION AQB_RMS.F_GetLogEntryNumber
(#LocationID int)
RETURNS INTEGER
AS
BEGIN
DECLARE
#MaxEntry Integer,
#EntryNumber Integer
Set #MaxEntry = (Select Max(SL.EntryNumber) FROM AQB_MON.AQB_RMS.SiteLog SL
WHERE SL.LocationID = #LocationID)
SET #EntryNumber = #MaxEntry + 1
RETURN #EntryNumber
END
My Trigger and attempt to use the Function -
CREATE TRIGGER [AQB_RMS].[SiteLogCreate] on [AQB_MON].[AQB_RMS].[AirVisionSiteLog]
AFTER INSERT
AS
BEGIN
declare #entrynumber int
declare #corrected int
set #corrected = 0
INSERT INTO [AQB_MON].[AQB_RMS].[SiteLog]
([SiteLogTypeID],[LocationID],[EntryNumber],[SiteLogEntry]
,[EntryDate],[Corrected],[DATE_CREATED],[CREATED_BY])
SELECT st.SiteLogTypeID, l.LocationID,
(select AQB_RMS.F_GetLogEntryNumber from [AQB_MON].[AQB_RMS].[SiteLog] sl
where sl.LocationID = l.LocationID)
, i.SiteLogEntry, i.EntryDate, #corrected, i.DATE_CREATED, i.CREATED_BY
from inserted i
left join AQB_MON.[AQB_RMS].[SiteLogType] st on st.SiteLogType = i.SiteLogType
left join AQB_MON.AQB_RMS.Location l on l.SourceSiteID = i.SourceSiteID
END
GO
I believe that you are close.
At this part of the query in the trigger: (I set the columns vertically so that the difference is more noticable)
SELECT st.SiteLogTypeID,
l.LocationID,
(select AQB_RMS.F_GetLogEntryNumber from [AQB_MON].[AQB_RMS].[SiteLog] sl where sl.LocationID = l.LocationID),
i.SiteLogEntry,
i.EntryDate,
#corrected,
i.DATE_CREATED,
i.CREATED_BY
...should be:
SELECT st.SiteLogTypeID,
l.LocationID,
AQB_RMS.F_GetLogEntryNumber(select l.LocationID from [AQB_MON].[AQB_RMS].[SiteLog] sl where sl.LocationID = l.LocationID),
i.SiteLogEntry,
i.EntryDate,
#corrected,
i.DATE_CREATED,
i.CREATED_BY
So basically, you would call the function name with the query as the parameter, which the results thereof should only be one row with a value.
Note that in my modified example, I added the l.LocationID after the select in the function call, so I'm not sure if this is what you need, but change that to match your needs. Because I'm not sure of the exact column that you need, add a comment should there be other issues.

Resources