JOIN and SELECT in SELECT issues - sql

I have 3 tables in which there are about 70 columns combined.
What I am trying to do is select everything from 1 and 2 but then there are certain conditions to get first and last score and those are in third table.
SELECT
ID, NAME, TIME, ROUNDS
(SELECT SCORE
FROM TABLE2 TAB2
JOIN TABLE3 TAB3 ON TAB2.ID = TAB3.ID
WHERE PARAMETER1 = 9 AND PARAMETER2 = 21) AS FIRST,
(SELECT SCORE
FROM TABLE2 TAB2
JOIN TABLE3 TAB3 ON TAB2.ID = TAB3.ID
WHERE PARAMETER1 = 15 AND PARAMETER2 = 2) AS LAST
FROM
TABLE1 TAB1
JOIN
TABLE2 TAB2 ON TAB1.ID = TAB2.ID
GROUP BY
ID, NAME, TIME, ROUNDS
I get the following error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I also tried to do the joins at the end and I do get a result but it is wrong
SELECT
ID, NAME, TIME, ROUNDS
(SELECT SCORE
WHERE PARAMETER1 = 9 AND PARAMETER2 = 21) AS FIRST,
(SELECT SCORE
WHERE PARAMETER1 = 15 AND PARAMETER2 = 2) AS LAST
FROM
TABLE1 TAB1
JOIN
TABLE2 TAB2 ON TAB1.ID = TAB2.ID
JOIN
TABLE3 TAB3 ON TAB2.ID = TAB3.ID
I get incorrect data because I cannot group it since it wants me to group parameters as well
Msg 8120, Level 16, State 1, Line 46
Column 'PARAMETER1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I really don't know what I'm doing wrong so is there any way you guys can help ?

As I mentioned in the comments, you need to correlate your subqueries, and I suspect that you think that all 3 references to TABLE2 TAB2 are the same instance of said object. This is a flawed understanding. Each time you reference an object it is a new "instance" of it.
As such what you likely want is this:
SELECT TAB1.ID,
TAB1.NAME,
TAB2.TIME,
TAB2.ROUNDS,
(SELECT TAB3.SCORE
FROM dbo.TABLE3 TAB3
WHERE TAB3.ID = TAB2.ID
AND TAB3.PARAMETER1 = 9
AND TAB3.PARAMETER2 = 21) AS FIRST,
(SELECT TAB3.SCORE
FROM dbo.TABLE3 TAB3
WHERE TAB3.ID = TAB2.ID
AND TAB3.PARAMETER1 = 15
AND TAB3.PARAMETER2 = 2) AS LAST
FROM dbo.TABLE1 TAB1
JOIN dbo.TABLE2 TAB2 ON TAB1.ID = TAB2.ID;
Or perhaps you would be better off with conditional aggregation, which would mean 1 scan of the table dbo.TABLE3:
SELECT TAB1.ID,
TAB1.NAME,
TAB2.TIME,
TAB2.ROUNDS,
MAX(CASE WHEN TAB3.PARAMETER1 = 9 AND TAB3.PARAMETER2 = 21 THEN TAB3.Score END) AS FIRST,
MAX(CASE WHEN TAB3.PARAMETER1 = 15 AND TAB3.PARAMETER2 = 2 THEN TAB3.Score END) AS FIRST
FROM dbo.TABLE1 TAB1
JOIN dbo.TABLE2 TAB2 ON TAB1.ID = TAB2.ID
JOIN dbo.TABLE3 TAB3 ON TAB2.ID = TAB3.ID
GROUP BY TAB1.ID,
TAB1.NAME,
TAB2.TIME,
TAB2.ROUNDS;
Note that the tables the columns belong to is guessed. If incorrect, you'll need to correct.

it seems below nested queries return multiple values .
(SELECT SCORE
WHERE PARAMETER1 = 9 AND PARAMETER2 = 21) AS FIRST
(SELECT SCORE
WHERE PARAMETER1 = 15 AND PARAMETER2 = 2) AS LAST
You should use top(1) with order by ascending for first and descending for last column as below.
SELECT
ID, NAME, TIME, ROUNDS
(SELECT top(1) SCORE
WHERE PARAMETER1 = 9 AND PARAMETER2 = 21 order by SCORE asc ) AS FIRST,
(SELECT top(1) SCORE
WHERE PARAMETER1 = 15 AND PARAMETER2 = 2 order by SCORE desc ) AS LAST
FROM
TABLE1 TAB1
JOIN
TABLE2 TAB2 ON TAB1.ID = TAB2.ID
JOIN
TABLE3 TAB3 ON TAB2.ID = TAB3.ID
I think above query helps you finding error on exact location.

Related

SQL Select all first items in a list of rows identified by Ids and filtered by a specific Type in another table

I need to create a table keyed by an ID where the values of one of the columns in the new table are the earliest values entered into the column of another table where the rows share the same ID and have a specific type label.
For example, say I want the Name and first Value entered for each fruit with an entry type A:
These are the tables I have:
TABLE1
Key
ID
Name
1
1
Cherry
2
2
Grape
TABLE2
Key
ID
Value
EntryNum
EntryType
1
1
21
1
A
2
1
32
2
B
3
1
4
3
B
4
1
15
4
A
5
2
3
1
B
6
2
8
2
A
7
2
16
3
B
And this is the result that I want:
TABLE3
ID
Name
EarliestEntry
1
Cherry
21
2
Grape
8
I've attempted the following query but it just returns the same value for all EarliestEntry:
SELECT TABLE1.ID, TABLE2.Name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY TABLE2.EntryNum)
as row_num, Value FROM TABLE2
WHERE TABLE2.ID = TABLE1.ID AND TABLE2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO TABLE3
FROM TABLE2
INNER JOIN TABLE1 ON TABLE1.ID = TABLE2.ID
GROUP BY TABLE1.ID, TABLE2.Type, TABLE2.EntryNum
I would greatly appreciate help on this. Thank you
If you wanted to use the ROW_NUMBER function then you would need to put that on TABLE1 and add a partition by like so:
WITH rn AS(
SELECT a.Key, ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY a.EntryNum) AS rn
FROM TABLE2 AS a
)
SELECT b.Name, a.Value AS EarliestValue
FROM TABLE2 AS a
INNER JOIN TABLE1 AS b ON b.ID = a.ID
INNER JOIN rn AS rn ON rn.key = a.key
WHERE rn.rn = 1
In your example you skipped the PARTITION BY clause so you just get a number for all values in TABLE2. Instead of a number per ID in ascending order for Value.
Based on your description of the three tables TABLE1, TABLE2 and TABLE3.
I modified a little bit your script. Thank of Dale K remark, I explain in some words the solution : the field TABLE2.Name shown in the first select was wrong, because [name] belongs to TABLE1, so the right syntax for this is TABLE1.name. And in the GROUP BY clause the field TABLE2.Type might be replaced by TABLE1.name to repect aggregation criteria. So the script becomes :
SELECT DISTINCT table1.id, table1.name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY table2.EntryNum)
as row_num, Value FROM table2
WHERE table2.id = table1.id AND table2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO table3
FROM table2
INNER JOIN table1 ON table1.id = table2.id
GROUP BY table1.id, table1.name, table2.entrynum;
Here, you can verify the output with fiddle
You are hugely over-complicating this.
Just partition Table2 and take a row-number, then join that to Table1 and filter on only row-number 1
SELECT
t1.Id,
t1.Name,
EarliestEntry = t2.Value
FROM Table1 t1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.EntryNum)
FROM Table2 t2
WHERE t2.EntryType = 'A'
) t2 ON t2.ID = t1.ID AND t2.rn = 1;
db<>fiddle

select the data from any of the four tables

I want to select the data from any of the four tables. Data can be available on any one out of four. Four table also will have data. two table also will have data. one table also will have data. please correct me below.
select top 100 t1.*
from
Table1 t1
left JOIN Table2 t2 on t1.EventId = t2.EventId
LEFT JOIN Table3 t3 ON t1.EventId = t3.EventId
LEFT JOIN Table4 t4 ON t1.EventId = t4.EventId
WHERE
t1.EventId = 12345 AND
t1.EditType = 'D' and
t2.EditType = 'D'and
t3.EditType = 'D' and
t4.EditType = 'D'
Putting the conditions in the WHERE clause turns outer joins into inner joins. Put the conditions in the ON clause
Select *
from Table1 t1
left JOIN Table2 t2
on t1.EventId = t2.EventId
and t2.EditType = 'D'
left JOIN Table3 t3
ON t1.EventId = t3.EventId
and t3.EditType = 'D'
left JOIN Table4 t4
ON t1.EventId = t4.EventId
and t4.EditType = 'D'
where t1.EventId = 12345
and t1.EditType = 'D'
If your tables have the same structure, you might be better doing a union all in a view or CTE (common table expression), then selecting from it instead of doing a left join - that way your information is appearing in separate records:
WITH FullData AS (
SELECT *, 1 AS TableSource FROM Table1
UNION ALL
SELECT *, 2 AS TableSource FROM Table2
UNION ALL
SELECT *, 3 AS TableSource FROM Table3
UNION ALL
SELECT *, 4 AS TableSource FROM Table4 )
SELECT * FROM FullData WHERE EventID = 12345 And EventType = 'D'
As per my example, you can also add in a source identifier to tell which table the information is being pulled from - as long as the structure remains the same throughout, the union all will work fine.

Update SQL query set

UPDATE tab1
SET col = 1
FROM tab1
LEFT JOIN tab2 ON tab2.ID = tab1.ID
WHERE tab2.ID IS NULL
Where do I put the ELSE col = 0in this query?
UPDATE tab1
SET col = CASE WHEN tab2.ID IS NULL THEN 1 ELSE 0 END
FROM tab1
LEFT JOIN tab2 ON tab2.ID = tab1.ID
I assume you want col to be 1 when tab2.ID is NULL and 0 when it is not. So you need to do 2 things. Use a case expression. Also remove your where expression so that you are not limiting the results table to only tab1 rows that have no relation to tab2

Joining with max date from table

SELECT COL1,
COL2,
COL3
FROM TABLE1,
TABLE2,
TABLE3,
TABLE4
WHERE TABLE1.KEY1 = TABLE2.KEY1
AND TABLE2.KEY = TABLE3.KEY
AND TABLE2.FILTER = 'Y'
AND TABLE3.FILTER = 'Y'
AND TABLE2.KEY = TABLE3.KEY
AND TABLE3.KEY = TABLE4.KEY
I have a similar query and I need to do modification, in a table 3 there is a date column and I need to pick highest day value row for joining. Lets say there are 4 rows from table number 3 which are getting satisfied for join, I need to pick highest date row out of those 4 for joining purpose and then show the result.
Hope question is clear. Database oracle 10g
Try something like this query.
SELECT
COL1,
COL2,
COL3,
T33.*
FROM TABLE1
JOIN TABLE2 ON TABLE1.KEY1 = TABLE2.KEY1
JOIN TABLE4 ON TABLE2.KEY = TABLE4.KEY
JOIN
(
SELECT MAX(T.Day) as DT, T.KEY
FROM TABLE3 T
WHERE T.FILTER = 'Y'
GROUP BY T.KEY
) T3 on TABLE4.KEY = T3.KEY
JOIN TABLE3 T33 ON T3.KEY = T33.KEY AND T3.DT = T33.Day
WHERE
TABLE2.FILTER = 'Y'
The main idea is that instead of
joining to TABLE3 you do this:
SELECT MAX(T.Day) as DT, T.KEY
FROM TABLE3 T
WHERE T.FILTER = 'Y'
GROUP BY T.KEY
give that table/recordset a name and join to it instead.
Then you can join again to the original TABLE3 (see T33)
to pull all the other needed columns from TABLE3 which are
not present in T3.
You can work out the other details, I think.
To minimally modify your current query, you can add a condition in your WHERE clause
AND TABLE3.DATE = (SELECT MAX(DATE) FROM TABLE3 WHERE TABLE3.FILTER = 'Y')
Although in the future I recommend using explicit JOINS.
SELECT COL1,
COL2,
COL3
FROM TABLE1
INNER JOIN TABLE2 ON TABLE1.KEY1 = TABLE2.KEY1
INNER JOIN TABLE3 ON TABLE2.KEY = TABLE3.KEY
INNER JOIN TABLE4 ON TABLE3.KEY = TABLE4.KEY
WHERE
TABLE2.FILTER = 'Y'
AND TABLE3.FILTER = 'Y'
AND TABLE3.DATE = (SELECT MAX(DATE) FROM TABLE3 WHERE TABLE3.FILTER = 'Y')

Fetching fetch the first occurrence from the result

I have an oracle sql query
select
distinct
tab1.col1,
tab2.col1
from
table1 tab1
join table2 tab2 on tab1.col1 = tab2.col1
Here i get the as expected in terms of distinct values.
For Example : The result rows are
1 2
3 4
5 6
Now I want to add one more join for table3. so my sql is
select
distinct
tab1.col1,
tab2.col1,
tab3.col1
from
table1 tab1
join table2 tab2 on tab1.col1 = tab2.col1
join table3 tab3 on tab1.col1 = tab3.col1
Here what the problem is is that table 3 is returning more than one value.
which is resulting in duplicate rows based on table3.
For Example : The result rows are
1 2 4
1 2 5
3 4 1
3 4 2
5 6 3
(Here if you notice row 1 & 2 are duplicate and 3 & 4 are duplicate)
What I am trying to do is for the join of table3 i want to fetch the
first occurrence of row.
This shud work for you !
select
distinct
tab1.col1,
tab2.col1,
MIN(tab3.col1)
from
table1 tab1
join table2 tab2 on tab1.col1 = tab2.col1
join table3 tab3 on tab1.col1 = tab3.col1
GROUP BY tab1.col1, tab2.col1
Edit: Thoughts,
I am assuming column 3 to be a integer which ever increasing, in that case this works. You can use the date column to define your aggregate accurately to get the "first occurance of your row".
select
distinct
tab1.col1,
tab2.col1,
t3.col1
from
table1 tab1
join table2 tab2 on tab1.col1 = tab2.col1
join (select distinct col1 from table3) t3 on tab1.col1 = t3.col1