return variable and CTE result in postgres - postgresql

I have a function that should return two integers: study asset store and origin asset store.
I have the study asset store saved as a variable from a query and then use CTE to find out what the origin asset store was. However, I'm getting errors trying to return both integers.
What would be the correct syntax for the query below to get the i_study_asset_store and asset_store_id back?
BEGIN
i_study_asset_store = (SELECT stud.asset_store_id FROM study.studies as stud WHERE studyid = _study_id::INT);
WITH study_store AS(
SELECT origin_asset_id
FROM asset.assets
WHERE asset_store_id = 23 --study_asset_store
LIMIT 1
),
origin_asset_store AS(
SELECT asset_store_id
FROM asset.assets
WHERE asset_id IN (SELECT origin_asset_id FROM study_store)
)
RETURN QUERY
SELECT
i_study_asset_store AS study_asset_store
,(SELECT asset_store_id FROM origin_asset_store) AS origin_asset_store;
END;

You got the syntax wrong. It should be
RETURN QUERY
WITH cte1 AS (/* ... */),
cte2 AS (/* ... */)
SELECT ...;

Related

Select distinct not working in complex pl/pgsql query

I have this query in a pl/pgsql function. I am using PostgreSQL 10.
FOR firstrecord IN
EXECUTE format(
'SELECT vans.id as vid, adidas.id as aid,
vans.color, adidas.color, vans.type, adidas.type
FROM shoes
FULL JOIN adidas ON shoes.id = adidas.id
FULL JOIN shoes ON shoes.id=vans.id
WHERE adidas.code = 607 and vans.code = 304 ' )
USING acode , vcode
END LOOP;
This works, but I would like to enforce a SELECT DISTINCT on vans.id AND
adidas.id.
This is the closest I got
FOR firstrecord IN
EXECUTE format(
'SELECT DISTINCT ON (adidas.id) vans.id as vid, adidas.id as aid,
vans.color, adidas.color, vans.type, adidas.type
FROM shoes
FULL JOIN adidas ON shoes.id = adidas.id
FULL JOIN shoes ON shoes.id=vans.id
WHERE adidas.code = 607 and vans.code = 304
ORDER BY adidas.id,vans.id' )
USING acode , vcode
END LOOP;
If I try to do something like SELECT DISTINCT ON (adidas.id, vans.id) the DISTINCT does not work, I get duplicates in result.
If I do SELECT DISTINCT vans.id as vid, adidas.id as aid , still the DISTINCT does not work, I get duplicates in result.
How do I fix this?
Thanks
As you're seeing now, if you use DISTINCT ON( expression1, expression2) it'll count all the combinations of the two expression as distinct, including when one is NULL as distinct from any non-NULL value. It seems like you want just one expression that takes in to account the ids from both tables. You can get this using the coalesce function, like so:
SELECT DISTINCT ON ( coalesce(adidas.id, vans.id)) vans.id as vid, adidas.id as aid,
vans.color, adidas.color, vans.type, adidas.type
FROM shoes
FULL JOIN adidas ON shoes.id = adidas.id
FULL JOIN vans ON shoes.id = vans.id
WHERE adidas.code = 607 and vans.code = 304
ORDER BY coalesce(adidas.id, vans.id)
This works in this case because if both are non-NULL, they should match one another, and if one is NULL the coalesce statement will return the non-NULL value.

How Dynamicaly columns in UNPIVOT operator

I currently have the following query:
WITH History AS (
SELECT
kz.*,
kz.__$operation AS operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS kz
INNER JOIN [cdc].[lsn_time_mapping] map
ON kz.[__$start_lsn] = map.start_lsn
where kz.GUID_BalanceHC_Zalezh = 'DDA9AB3A-A0AF-4623-9362-0000C8C83D63'
),
UnpivotedValues AS(
SELECT guid, GUID_another, field, val, operation, beginT, endT
FROM History
UNPIVOT ( [val] FOR field IN
(
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
etc...
))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY guid, GUID_another, field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL ORDER BY guid
This query returns the changed values for a single table that has CDC (Change Data Capture) enabled.
I want to create a stored procedure that receives the columns to be unpivoted, and the cdc function (e.g. cdc.fn_cdc_get_all_...) as parameters and returns the result set.
The result for this tables must be joined in one report.
In my case parameter 1 is cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all'). This is the CDC function.
How should I send the list of fields that i want in the result? How's the string?
Also, is there a way to do without dynamic SQL? Dynamic SQL it is not better solution for performance.
As you know SQL Server is declarative by design and does not support macro substitution.
UNPIVOT would clearly be more performant, but here is a simplified example of a UNPIVOT which does not require Dynamic SQL, but only a little XML.
Example
Let's assume your table/results looks like this:
You may notice that I only we only specify key fields to EXCLUDE in the final WHERE
Declare #YourData table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50),Salary decimal(10,2))
Insert into #YourData values
(1,1,'John','Smith','john.smith#email.com',85600),
(2,0,'Jane','Doe' ,'jane.doe#email.com',83200)
;with cte as (
-- Replace with your Complex Query
Select * from #YourData
)
Select A.ID
,A.Active
,C.*
From cte A
Cross Apply (Select XMLData=cast((Select A.* for XML RAW) as xml)) B
Cross Apply (
Select Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From XMLData.nodes('/row') C1(n)
Cross Apply C1.n.nodes('./#*') C2(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','Active')
) C
Returns

Convert an SQL query with row_number to AR code

I have somewhat complex SQL query that I need to convert to nice and clean AR code, and I'm having some troubles with it.
Here's the query:
SELECT a.*
FROM
fixed_assets a
INNER JOIN
(
SELECT e.id, e.fixed_asset_id
FROM
fixed_asset_book_entries e
WHERE e.book_id = %SOME_VALUE_1%
) e_mod
ON e_mod.fixed_asset_id = a.id
INNER JOIN
(
SELECT s.fixed_asset_book_entry_id,
s.status,
ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
FROM
status_changes s
WHERE s.created_at < %SOME_VALUE_2%
) s_mod
ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;
So, the point of it all is to extract such fixed_assets rows, that have related fixed_asset_book_entries with certain book_id, and it's last status_change before certain date has any status except inactive.
What I want to end up with is a class-level (scope?) method FixedAsset.active_within_book_on_date(book_id, date), that will return FixedAsset objects, that comply with restrictions I've explained above. I'm familiar with joins method, but I'm not sure how to handle row_number function except passing raw SQL to joins call.
I think the best you can do is something like the following. In lib/sql_template.rb:
class SqlTemplate
attr_reader :sql
# Load the file and process the ERB
# Call it like this:
# sql = SqlTemplate.new(filename, binding)
def initialize(filename, the_binding)
raw_code = File.read(File.join(Rails.root, 'lib/sql', filename))
template = ERB.new(raw_code)
#sql = template.result(the_binding)
end
end
Then define your raw SQL in lib/sql/active_within_book_on_date.sql. Which would then allow you to do this:
class FixedAsset
def self.active_within_book_on_date(book_id, date)
template = SqlTemplate.new('active_within_book_on_date.sql', binding)
self.find_by_sql(template.sql)
end
end
Your SQL file would look like this:
SELECT a.*
FROM
fixed_assets a
INNER JOIN
(
SELECT e.id, e.fixed_asset_id
FROM
fixed_asset_book_entries e
WHERE e.book_id = <%=book_id%>
) e_mod
ON e_mod.fixed_asset_id = a.id
INNER JOIN
(
SELECT s.fixed_asset_book_entry_id,
s.status,
ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
FROM
status_changes s
WHERE s.created_at < '<%=date%>'
) s_mod
ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;
That's probably as 'nice and clean' as you can get.

Using EXISTS as a column in TSQL

Is it possible to use the value of EXISTS as part of a query?
(Please note: unfortunately due to client constraints, I need SQLServer 2005 compatible answers!)
So when returning a set of results, one of the columns is a boolean value which states whether the subquery would return any rows.
For example, I want to return a list of usernames and whether a different table contains any rows for each user. The following is not syntactically correct, but hopefully gives you an idea of what I mean...
SELECT T1.[UserName],
(EXISTS (SELECT *
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName])
) AS [RowsExist]
FROM [UserTable] T1
Where the resultant set contains a column called [UserName] and boolean column called [RowsExist].
The obvious solution is to use a CASE, such as below, but I wondered if there was a better way of doing it...
SELECT T1.[UserName],
(CASE (SELECT COUNT(*)
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName]
)
WHEN 0 THEN CAST(0 AS BIT)
ELSE CAST(1 AS BIT) END
) AS [RowsExist]
FROM [UserTable] T1
Your second query isn't valid syntax.
SELECT T1.[UserName],
CASE
WHEN EXISTS (SELECT *
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName]) THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END AS [RowsExist]
FROM [UserTable] T1
Is generally fine and will be implemented as a semi join.
The article Subqueries in CASE Expressions discusses this further.
In some cases a COUNT query can actually perform better though as discussed here
I like the other guys sql better but i just wrote this:
with bla as (
select t2.username, isPresent=CAST(1 AS BIT)
from t2
group by t2.username
)
select t1.*, isPresent = isnull(bla.isPresent, CAST(0 AS BIT))
from t1
left join blah on t1.username=blah.username
From what you wrote here I would alter your first query into something like this
SELECT
T1.[UserName], ISNULL(
(
SELECT
TOP 1 1
FROM [AnotherTable]
WHERE EXISTS
(
SELECT
1
FROM [AnotherTable] AS T2
WHERE T1.[UserName] = T2.[UserName]
)
), 0)
FROM [UserTable] T1
But actually if you use TOP 1 1 you would not need EXISTS, you could also write
SELECT
T1.[UserName], ISNULL(
(
SELECT
TOP 1 1
FROM [AnotherTable] AS T2
WHERE T1.[UserName] = T2.[UserName]
), 0)
FROM [UserTable] T1

TSQL Update Query behaving unexpectedly

I have a nested select query that is returning the proper amount of rows. The query builds a recordset and compares it to a table and returns the records in the query that are not in the table.
I converted the select query to an update query. I am trying to populate the table with the rows returned from the query. When I run the update query it is returning with zero rows to update. I dont understand why because the select query is returning record and I am using the same code in the update query.
Thanks
Select Query: (This is returning several records)
Select *
From
(SELECT DISTINCT
ProductClass,SalProductClass.[Description],B.Branch,B.BranchDesc,B.Salesperson,B.Name,
CAST(0 AS FLOAT) AS Rate,'N' AS Split
FROM (SELECT SalBranch.Branch,SalBranch.[Description] AS BranchDesc,A.Salesperson,A.Name
FROM (SELECT DISTINCT
Salesperson,Name
FROM SalSalesperson
) A
CROSS JOIN SalBranch
) B
CROSS JOIN SalProductClass
) C
Left Outer Join RateComm On
RateComm.ProductClass = C.ProductClass and
RateComm.Branch = C.Branch And RateComm.Salesperson = C.Salesperson
Where RateComm.ProductClass is Null
Update Query: (This is returning zero records)
UPDATE RateComm
SET RateComm.ProductClass=C.ProductClass,RateComm.ProdClassDesc=C.ProdClassDesc,
RateComm.Branch=C.Branch,RateComm.BranchDesc=C.BranchDesc,RateComm.Salesperson=C.Salesperson,
RateComm.Name=C.Name,RateComm.Rate=C.Rate,RateComm.Split=C.Split
FROM (SELECT DISTINCT
ProductClass,SalProductClass.[Description] AS ProdClassDesc,B.Branch,B.BranchDesc,B.Salesperson,B.Name,
CAST(0 AS FLOAT) AS Rate,'N' AS Split
FROM (SELECT SalBranch.Branch,SalBranch.[Description] AS BranchDesc,A.Salesperson,A.Name
FROM (SELECT DISTINCT
Salesperson,Name
FROM SalSalesperson
) A
CROSS JOIN SalBranch
) B
CROSS JOIN SalProductClass
) C
LEFT OUTER JOIN RateComm ON C.ProductClass=RateComm.ProductClass AND
C.Salesperson=RateComm.Salesperson AND C.Branch=RateComm.Branch
WHERE RateComm.ProductClass IS NULL
It's difficult to update what doesn't exist. Have you tried an INSERT query instead?