postgres sql statement with subquery - sql

Below is some pseudocode that I need to retrieve data from a postgresql database. The subquery is where I am having trouble figuring out how to do correctly.
I need to include any record that has that has an output_type='TEXT' and if the output_type does not have 'TEXT' use a different criteria to ensure it is included.
Select * from tablename
left join table2 on table2.fk_id = tablename.pkid
where table2.letterid = 5
and table2.pkid not exist( select * from table3 where
if the table3.output = 'text'
include the record
else not table3.output = 'text'
if table3.sentdate is null
the record)

NOT EXISTS ( is how I do this generally. You can join with the other statement's tables in there, as I'm doing in this example with table2.pkid.
SELECT * FROM tablename
LEFT JOIN table2 ON (
table2.fk_id = tablename.pkid
AND table2.letterid = 5
AND NOT EXISTS (
SELECT * FROM table3
WHERE table3.pkid = table2.pkid
AND ... -- I don't quite follow what you're trying to do with the if/else, but it'd go here
)
);
table2.pkid NOT IN (SELECT table3.pkid ... also works for this. Not sure if it'll have the same performance or not, but personally I prefer the flexibility of NOT EXISTS anyway.

Related

Snowflake, SQL where clause

I need to write query with where clause:
where
pl.ods_site_id in (select id from table1 where ...)
But if subquery (table1) didn't return anything, where clause doesn't need to include in result query (like it returns TRUE).
How can I do it? (I have snowflake SQL dialect)
You could include a second condition:
where pl.ods_site_id in (select id from table1 where ...) or
not exists (select id from table1 where ...)
This explicitly checks for the subquery returning no rows.
If you are willing to use a join instead, Snowflake supports qualify clause which might come in handy here. You can run this on Snowflake to see how it works.
with
pl (ods_site_id) as (select 1 union all select 5),
table1 (id) as (select 5) --change this to 7 to test if it returns ALL on no match
select a.*
from pl a
left join table1 b on a.ods_site_id = b.id -- and other conditions you want to add
qualify b.id = a.ods_site_id --either match the join condition
or count(b.id) over () = 0; --or make sure there is 0 match from table1

Pass values as parameter from select query

I want to pass values from output of select query to another query. Basically both queries will be part of a stored procedure. e.g.
select Id, RelId
from tables
There will be multiple rows returned by above query and I want to pass them to the following query
select name
from table2
where Id = #Id and MgId = #RelId
Please suggest
You cannot pass multiple values in SQL.
But maybe you can just join your 2 tables, that would be far more efficient.
Not knowing your table schemes I suggest something like this. You might have to adapt this to your actual table schemas off course
select name
from table2 t2
inner join tables t on t2.Id = t.Id
and t2.MgId = t.RelId
EDIT
As Gordon mentioned in his answer, this approach can show double rows in your result.
If you don't want that than here are 2 ways of getting rid of the doubles
select distinct name
from ...
or by grouping by adding this at the end of the statement
group by name
Though this will work, avoiding the doubles like in Gordon's answer is better
I would suggest using exists:
select t2.name
from table2 t2
where exists (select 1
from tables t
where t2.Id = t.Id and t2.MgId = t.RelId
);
The difference between exists and join is that this will not generate duplicates, if there are multiple matches between the tables.
Or...
SELECT *
INTO #Table1
FROM ...
SELECT *
INTO #Table2
FROM ...
SELECT *
FROM #Table1 T1
JOIN #Table2 T2
DROP TABLE #Table1, #Table2

SQL subquery to joins -

Is it possible to remove the subquery from this SQL?
Table has 2 attributes "id" and "field"
Many field could have the same Id.
These table has many registers with the same Id and different Value
In need get all same Id values using one of them like filter.
select *
from Table
where id = (select id from Table where value = 'someValue')
I think it could be really easy but I don't know how to do.
Self Join can be done
select T.Id,T.Field
from Table T
INNER JOIN Table TT
ON T.ID = TT.ID
AND TT.Value = 'someValue'
Not sure if you over simplified your example too much but you could make this a little simpler.
select *
from Table
where value = 'someValue'
This should work
select T1.* from Table T1 JOIN Table T2 ON T1.id = T2.id AND T2.value = 'someValue'
Edited (Correct Answer):
What I assume your problem is:
You have a value. Let´s pretend it´s "testValue". Now you want to get the id of this value and find all other datasets with the same id.
What has to be cleared is that, "ID" is not the Primary Key and is not Unique.
You should be able to solve this by a simple self join:
select t.* from Table t right join Table tt on tt.id = t.id where tt.value = 'someValue';
So because of the join you will get a result that returns simply the table. With the where clause you shrink the result to your value. You should get the set of ids.
Old Answer:
This should do the trick:
select * from Table a inner join Table2 b on a.id = b.id where b.value = 'someValue';
You mentioned only one table in your question. I think this must be a mistake. If not, you have to change only the Table2 in my query. But that would have no sense as you could do a simple query, too:
select * from Table where value = 'someValue';
this would be the result of the first query with a self join.

SQL Server--Is it possible to work around using a temporary table for a query that filters based on an alias case column?

I am trying to alter a base query that selects data from several joined tables, and filters out rows based on the CASE WHEN below. The result set is to be returned as follows:
If all of the rows return 0 in the CASE column, return one line with '0' in the OVERDUE column (the "return one line" portion is taken care of by DISTINCT.)
If any of the rows return 1 for the CASE column, return one line with '1' in the OVERDUE column.
The base is as follows:
SELECT DISTINCT t1.*,
CASE WHEN t3.MTemp > t3.MTempLimit
then 1
when t3.TotHours > t3.THoursLimit
then 1
else 0
end [Overdue]
from table_1 t1
LEFT JOIN table_2 t2 on t1.ResNo = t2.ResNo and t1.PCode = t2.PCode
LEFT JOIN table_3 t3 on t2.RepJobNo = t3.RepJobNo
LEFT JOIN table_4 t4 on t4.TypeID = t2.RepType
WHERE t2.RepStat = 1
The catch is, I've already created a working version of this by using a temp table and doing a IF EXISTS/ELSE query on the temp table's OVERDUE column. However, I've been informed that this solution may not be useable (due to having to go through certain front-end software).
Is it possible to do a workaround for this that does not involve using a temporary table? I've been making attempts at using both a derived table and CTEs, neither of which have yielded anything usable, due to the fact that one cannot use IF/ELSE clauses after those (which was what I was counting on).
I'm still getting the hang of T-SQL, so any help would be greatly appreciated.
Sounds like a simple ROW_NUMBER() and a couple of CTEs will work:
;WITH RS1 as (
SELECT t1.*,
CASE WHEN t3.MTemp > t3.MTempLimit
then 1
when t3.TotHours > t3.THoursLimit
then 1
else 0
end [Overdue]
from table_1 t1
LEFT JOIN table_2 t2 on t1.ResNo = t2.ResNo and t1.PCode = t2.PCode
LEFT JOIN table_3 t3 on t2.RepJobNo = t3.RepJobNo
LEFT JOIN table_4 t4 on t4.TypeID = t2.RepType
WHERE t2.RepStat = 1
), RS2 as (
select *,ROW_NUMBER() OVER (ORDER BY Overdue DESC) rn
from RS1
)
select * from RS2 where rn = 1
(There's no need for a DISTINCT now that we're only returning one row)
In general any temporary table referenced in another query can simply be substituted for as follow, so that this:
insert #temp
select -- definition of temptable
;
select ...
from #temp
join ...
becomes
select
from (
-- definition of temptable
) temp
join ...

An issue possibly related to Cursor/Join

Here is my situation:
Table one contains a set of data that uses an id for an unique identifier. This table has a one to many relationship with about 6 other tables such that.
Given Table 1 with Id of 001:
Table 2 might have 3 rows with foreign key: 001
Table 3 might have 12 rows with foreign key: 001
Table 4 might have 0 rows with foreign key: 001
Table 5 might have 28 rows with foreign key: 001
I need to write a report that lists all of the rows from Table 1 for a specified time frame followed by all of the data contained in the handful of tables that reference it.
My current approach in pseudo code would look like this:
select * from table 1
foreach(result) {
print result;
select * from table 2 where id = result.id;
foreach(result2) {
print result2;
}
select * from table 3 where id = result.id
foreach(result3) {
print result3;
}
//continued for each table
}
This means that the single report can run in the neighbor hood of 1000 queries. I know this is excessive however my sql-fu is a little weak and I could use some help.
LEFT OUTER JOIN Tables2-N on Table1
SELECT Table1.*, Table2.*, Table3.*, Table4.*, Table5.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.ID
LEFT OUTER JOIN Table3 ON Table1.ID = Table3.ID
LEFT OUTER JOIN Table4 ON Table1.ID = Table4.ID
LEFT OUTER JOIN Table5 ON Table1.ID = Table5.ID
WHERE (CRITERIA)
Join doesn't do it for me. I hate having to de-tangle the data on the client side. All those nulls from left-joining.
Here's a set-based solution that doesn't use Joins.
INSERT INTO #LocalCollection (theKey)
SELECT id
FROM Table1
WHERE ...
SELECT * FROM Table1 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table2 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table3 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table4 WHERE id in (SELECT theKey FROM #LocalCollection)
SELECT * FROM Table5 WHERE id in (SELECT theKey FROM #LocalCollection)
Ah! Procedural! My SQL would look like this, if you needed to order the results from the other tables after the results from the first table.
Insert Into #rows Select id from Table1 where date between '12/30' and '12/31'
Select * from Table1 t join #rows r on t.id = r.id
Select * from Table2 t join #rows r on t.id = r.id
--etc
If you wanted to group the results by the initial ID, use a Left Outer Join, as mentioned previously.
You may be best off to use a reporting tool like Crystal or Jasper, or even XSL-FO if you are feeling bold. They have things built in to handle specifically this. This is not something the would work well in raw SQL.
If the format of all of the rows (the headers as well as all of the details) is the same, it would also be pretty easy to do it as a stored procedure.
What I would do: Do it as a join, so you will have the header data on every row, then use a reporting tool to do the grouping.
SELECT * FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.resultid -- this could be a left join if the table is not guaranteed to have entries for t1.id
INNER JOIN table2 t3 ON t1.id = t3.resultid -- etc
OR if the data is all in the same format you could do.
SELECT cola,colb FROM table1 WHERE id = #id
UNION ALL
SELECT cola,colb FROM table2 WHERE resultid = #id
UNION ALL
SELECT cola,colb FROM table3 WHERE resultid = #id
It really depends on the format you require the data in for output to the report.
If you can give a sample of how you would like the output I could probably help more.
Join all of the tables together.
select * from table_1 left join table_2 using(id) left join table_3 using(id);
Then, you'll want to roll up the columns in code to format your report as you see fit.
What I would do is open up cursors on the following queries:
SELECT * from table1 order by id
SELECT * from table1 r, table2 t where t.table1_id = r.id order by r.id
SELECT * from table1 r, table3 t where t.table1_id = r.id order by r.id
And then I would walk those cursors in parallel, printing your results. You can do this because all appear in the same order. (Note that I would suggest that while the primary ID for table1 might be named id, it won't have that name in the other tables.)
Do all the tables have the same format? If not, then if you have to have a report that can display the n different types of rows. If you are only interested in the same columns then it is easier.
Most databases have some form of dynamic SQL. In that case you can do the following:
create temporary table from
select * from table1 where rows within time frame
x integer
sql varchar(something)
x = 1
while x <= numresults {
sql = 'SELECT * from table' + CAST(X as varchar) + ' where id in (select id from temporary table'
execute sql
x = x + 1
}
But I mean basically here you are running one query on your main table to get the rows that you need, then running one query for each sub table to get rows that match your main table.
If the report requires the same 2 or 3 columns for each table you could change the select * from tablex to be an insert into and get a single result set at the end...