postgres: query results into variables to be used later - sql

I have what will end up being a complex query where certain parts will often be repeated. I would therefore like to store the results of some sub queries into variables which can then be used in the main query.
For example I would like to set the variable 'variable_id' to be equal to a SELECT query and variable_school_id to be equal to another SELECT query:
variable_id integer := (SELECT id FROM account WHERE email = 'test#test.com');
variable_school_id integer := (SELECT school FROM account WHERE email = 'test#test.com');
Then I would like to make use of those variables in a query that would look like:
select * from doctor where account_id = variable_id AND school = variable_school_id ;
How do I go about doing this?

Can't you just use CTEs?
with params as (
SELECT id, school
FROM account
WHERE email = 'test#test.com'
)
select d.*
from params cross join
doctor d
on d.account_id = params.id and d.school = params.school;

Related

Use WHERE + AND + CASE + IN when crafting T-SQL

I have a stored procedure that has a few variables that may or may not be passed. they are a list of PKs from other tables, so FKs but formatted in as a string of CSVs.
here's what the query essentially looks like
DECLARE #SomeIds VARCHAR(MAX) = ''
CREATE TABLE #TempIds (Id INT)
IF (#SomeIds = '' OR #SomeIds = NULL) INSERT INTO #TempIds VALUES (NULL)
ELSE INSERT INTO #TempIds SELECT * FROM SplitString(#SomeIds,',') -- SplitString() is a user function
SELECT cont.varchar_LastName AS LastName
,cred.varchar_CredentialName AS CredentialName
FROM [dbo].[tbl_Contacts] AS cont
LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc ON cont.pk_int_Id = cc.fk_ContactId
LEFT JOIN [dbo].[tbl_Credentials] AS cred ON cc.fk_CredentialId = cred.pk_int_Id
So this query basically gives me a full list of contacts both with and without a credential name. I don't have a WHERE clause, so not surprised.
I get data basically like:
LastName | CredentialName
---------------------------
Stevens | Admin
Arnolds | User
Bishop | NULL
Evans | NULL
So if I add a WHERE clause like this:
WHERE cred.pk_int_Id IN (SELECT * FROM #TempIds)
I get zero results.
When I run this:
SELECT * FROM #TempIds
I get this:
Id
-----------
NULL
When I run it with "real values" in #SomeIds like '1,2' then it works fine.
I presume this is because my WHERE clause is looking in the cred table and there are no NULL values in that table, so that's why I'm not getting anything.
But I'm not sure how I fix it?
I guess I really want to do something like this:
WHERE CredentialName IN (SELECT * FROM #TempIds)
But I believe to do that, I'd have to run the first query into another temp table, then run a second query on that table.
Any help is greatly appreciated.
You can avoid a temp table
WHERE (NULLIF(#SomeIds,'') IS NULL OR cred.pk_int_Id IN (SELECT value FROM SplitString(#SomeIds,',')))
Or if your Sql Server version supports STRING_SPLIT
WHERE (NULLIF(#SomeIds,'') IS NULL OR cred.pk_int_Id IN (SELECT value FROM STRING_SPLIT(#SomeIds,',')))
And then don't initialize #SomeIds to make it get all records.
I would consider using a UNION for this. Putting an OR in a WHERE clause can make for some bad execution plans, and often makes indexes unusable.
SELECT cont.varchar_LastName AS LastName
,cred.varchar_CredentialName AS CredentialName
FROM [dbo].[tbl_Contacts] AS cont
LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc
ON cont.pk_int_Id = cc.fk_ContactId
LEFT JOIN [dbo].[tbl_Credentials] AS cred
ON cc.fk_CredentialId = cred.pk_int_Id
WHERE cred.pk_int_Id IN (SELECT value FROM STRING_SPLIT(#SomeIds,','))
UNION ALL
SELECT cont.varchar_LastName AS LastName
,cred.varchar_CredentialName AS CredentialName
FROM [dbo].[tbl_Contacts] AS cont
LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc
ON cont.pk_int_Id = cc.fk_ContactId
LEFT JOIN [dbo].[tbl_Credentials] AS cred
ON cc.fk_CredentialId = cred.pk_int_Id
WHERE NULLIF(#SomeIds,'') IS NULL
I talk about using OR in UPDATES statements here. But the same logic applies to SELECT.

sql query for multi valued attributes

I have resources each represented by a guid and they have attribute name-value pairs. I would like to query
for resources which have the given attribute name value pairs.
So, suppose the table looks like:
GUID ATTR_SUBTYPE ATTR_VAL
63707829116544a38c5a508fcde031a4 location US
63707829116544a38c5a508fcde031a4 owner himanshu
44d5bf579d9f4b9a8c41429d08fc51de password welcome1
44d5bf579d9f4b9a8c41429d08fc51de host retailHost
c67d8f5d1a9b41428f029d55b79263e1 key random
c67d8f5d1a9b41428f029d55b79263e1 role admin
and I want all the resources with location as US and owner as olaf.
One possible query would be:
select guid from table where attr_subtype = 'location' and attr_value = ‘US'
INTERSECT
select guid from table where attr_subtype = 'owner' and attr_value = ‘himanshu';
There can be any number of attribute name value pairs in the query, so an additional intersection per pair
in the query. I was wondering if we can construct a better query as intersection is expensive.
Assuming you don't have duplicate attributes per GUID you can achieve the desired result without a JOIN:
SELECT "GUID" FROM T
WHERE ( "ATTR_SUBTYPE" = 'location' AND "ATTR_VAL" = 'US' )
OR ( "ATTR_SUBTYPE" = 'owner' AND "ATTR_VAL" = 'himanshu' )
GROUP BY "GUID"
HAVING COUNT(*) = 2 -- <-- keep only GUID have *both* attributes
See http://sqlfiddle.com/#!4/80900/2
Generally, JOIN would be better than INTERSECT here. It gives a chance to get first records prior than several full table scans will finish. But anyway you select a slow data structure so it wouldn't wonderful if it slowdown.
Try something like
select *
from
(select * from table where attr_subtype = 'location' and attr_value = 'US') t1
join
(select * from table where attr_subtype = 'owner' and attr_value = 'himanshu') t2
on (t1.guid = t2.guid)
...
Insert your targets into a temp table then join to it.
select t.guid
from table as t
join temp
on t.attr_subtype = temp.attr_subtype
and t.attr_value = temp.attr_value

improving sql query in the case of many ORs

I have an SQL query which can have around 1.5k of IDs OR'ed in the following form. They are the IDs of people I have and they are individually selectable, hence the many ORs when everyone is selected. Is it better to just SELECT everyone's ID then put it in the AND condition, UNION instead of OR, or perhaps use IN to bunch up all IDs, since the user may select everyone accept 1 person. Thanks.
...
AND (
(UserID = '53b95690-22d8-44a2-ad56-919cb4037218')
OR (UserID = '87b7fc0c-28f4-4f2e-9909-066df42245fa')
OR (UserID = '98c1b5e3-6ba5-4bd9-b8f5-d3b2221e3e3a')
OR...
EDIT: The complete query, as requested;
SELECT *
FROM Mail WITH (NOLOCK)
WHERE Active= 1
AND Company= '1d034e8b-0122-4531-8795-895d9287920f'
AND (
CompanyDivisionID= '129bcca1-b1d8-4a9e-8152-0b9e936c9d01'
OR CompanyDivisionID= '1bf4023d-22a3-4520-b751-7842576f42b7'
)
AND (
(DestinationUserID = '53b95690-22d8-44a2-ad56-919cb4037218')
OR (DestinationUserID = '87b7fc0c-28f4-4f2e-9909-066df42245fa')
OR (DestinationUserID = '98c1b5e3-6ba5-4bd9-b8f5-d3b2221e3e3a')
...
...
...
OR (DestinationUserID = '8c78fc05-7969-48fd-9b30-774e5d9a70bd')
OR (DestinationUserID = 'e7b76096-fe7d-44b8-9158-8293ac609471')
OR (DestinationUserID = '8a6b7385-4339-43fb-b95b-a7b687982bcd')
)
ORDER BY SendingDate DESC
Create a (temporary) table containing your selected UserIDs and join to that table.
SELECT *
FROM Mail WITH (NOLOCK)
inner join SelectedUsers on mail.DestinationUserID = SelectedUsers.UserID
WHERE Active= 1

SQL - GROUPING, ID, NAME

I was wondering what the answer should be for 1c. on this website:
http://sqlzoo.net/6.htm
SELECT company
FROM route WHERE stop=4
GROUP BY name, ID
this obviously isn't working, the ID and name isn't showing up no matter what. What is missing here? Thanks.
SELECT stops.id, stops.name
FROM route
INNER JOIN stops on route.stop = stops.id
WHERE route.num = 4 AND route.company = 'LRT'
You need to join the tables as the data you want to return is in a different table to the one which filters the data.
This works and does not include any unnecessary table joins. A good rule of thumb is to use EXISTS to verify values in a table that you do not need the output for. Otherwise, you would use a JOIN
SELECT stops.id, stops.name
FROM stops
WHERE EXISTS
(
SELECT 1 FROM route
WHERE route.stop = stops.id AND num = '4' AND company = 'LRT'
)
select s.id, s.name
from stops s
inner join route r
on s.id = r.stop
where r.num= 4
AND r.company= 'LRT'
It gives you this error:
sql: Unknown column 'name' in 'group statement'
There is no name in route tabel.
The tables structure are:
stops(id, name)
route(num,company,pos, stop)
So the answer for this quiz is:
SELECT s.id, s.name
FROM route r, stops s
WHERE r.stop= s.id
and r.num = 4 AND r.company = 'LRT'

Can you rename a table using the keyword 'AS' and a select statement?

This is probably a stupid question to most of you but I was wondering whether you can rename a column using the 'AS' keyword and a select statement?
Here is my SQL:
Select Main.EmpId
, Associate_List.costCenter, Assignments.Area
, Main.Assignments_1 AS (
Select Assignment_Name
from Assignments
where Assignment_Number = 1
and Assignments.Area = '#Someparemeter'
)
from associate_list
, main
, APU_CC
, Assignments
where Main.Empid = Associate_List.Empid
and substring(Associate_List.CostCenter,1,4) = APU_CC.CostCentre
The only part of SQL I'm wondering about is:
Main.Assignments_1 AS (
Select Assignment_Name
from Assignments
where Assignment_Number = 1
and Assignments.Area = '#Someparemeter'
)
Is this possible or am I talking jibberish or is this just a stupid thing to do?
Many Thanks
The part after as is not a value but a variable name; the SQL database will use it to reference the value of the result set so you can compare/sort/filter them. Therefore this is not possible.
If you must do this, you must read the documentation of your database how to build dynamic queries. But I suggest against it because it will cause strange errors that will be very hard to debug.
I'm not quite sure what you are driving at.
If there is a column in the Main table called Assignments_1, you can rename it in your query (to give a different header at the top of the output or for some other reason) like this...
SELECT MAIN.ASSIGNMENT_1 AS MY_NEW_NAME
FROM etc.
If you want a derived table in your query you name it like this...
SELECT MAIN.ASSIGNMENT_1,
SELECT *
FROM (SELECT THIS, THAT, THE_OTHER
FROM SOME_TABLE) AS DERIVED_TABLE
FROM etc.
If you didn't want either of those things, please clarify and we'll try to help.
In SQL Server, you can assign an alias to a column with AS like so:
...
ColumnName AS ColumnAlias,
...
And you can do it for a "sub-select" like you have in your example. (I wouldn't write the query quite like that--I'd run the subquery first and then plop the result into the second query like so:
DECLARE #Assignment_Name varca(100) -- or however long
SELECT #Assignment_Name = Assignment_Name
from Assignments
where Assignment_Number = 1
and Assignments.Area = #Someparemeter
SELECT
...
#Assignment_Name = Assignment_Name,
...
But you can do this:
Select m.EmpId, l.costCenter,
(Select Area From Assignments a
Where Assignment_Number = 1
And Area = '#Someparemeter') As Area,
(Select Assignment_Name From Assignments a
Where Assignment_Number = 1
And Area = '#Someparemeter') As Assignments_1
From associate_list l
Join main m On m.Empid = l.Empid
Join APU_CC c On c.CostCentre = substring(l.CostCenter,1,4)
or this:
Select m.EmpId, l.costCenter, Asgn.Area,
Asgn.Assignment_Name as Assignments_1
From associate_list l
Join main m On m.Empid = l.Empid
Join APU_CC c On c.CostCentre = substring(l.CostCenter,1,4)
Cross Join (Select Assignment_Name From Assignments a
Where Assignment_Number = 1
And Area = '#Someparemeter') as Asgn