get modified result from 2 tables - sql

I have 2 tables:
table1
id | name
-----------------------------------------
1 | abc
2 | def
table2
table1id | nr_name | nr_val
-----------------------------------------
1 | 7 | 123
1 | 9 | 321
2 | 7 | 432
What SQL code do I need to get this result?
result
id | name | nr7 | nr9
-----------------------------------------
1 | abc | 123 | 321
2 | def | 432 | 0
nr_name can only be 7 or 9 and I can't change the structure of table2
(I will add more nr_name types later).

Try this:
sELECT
t1.id,
t1.name,
MAX(CASE WHEN t2.nr_name = 7 THEN t2.nr_val END) AS 'nr7',
MAX(CASE WHEN t2.nr_name = 9 THEN t2.nr_val END) AS 'nr9'
FROM table1 AS t1
INNER JOIN table2 AS t2 ON t1.id = t2.table1id
WHERE t2.name IN (7, 9)
GROUP BY t1.id, t1.name

SELECT t1.id,t1.name,
CASE WHEN t2.nr_name = 7 THEN t2.nr_val ELSE 0 END AS 'nr7',
CASE WHEN t2.nr_name = 9 THEN t2.nr_val ELSE 0 END AS 'nr9'
FROM table1 AS t1
INNER JOIN table2 AS t2 ON t1.id = t2.table1id
WHERE t2.name IN (7,9)
GROUP BY t1.id, t1.name

Related

Oracle SQL - How Can I Join Two Tables, One to Many?

I am looking to do a full outer join in order to get the following results. Mainly I am joining table 1 to table 2.
However in table 1, anything that has a 0 in Column A but has the same available value in Key 1 column (ABC100 Key 1 value in table 1), only use that record (record 1 in table 1), and ignore 0 record (record 2 in table 1)
When joined to table 2, specifically for ABC100, I am expecting to see output lines 1 and 2 in the expected table results.
Any help or ideas going about this?
Example:
Table 1
| Key 1 | Column A |
| ABC100 | 100 |
| ABC100 | 0 |
| ABC300 | 200 |
| ABC400 | 300 |
Table 2
| Key 2 | Column C |
| ABC100 | 100 |
| ABC200 | 50 |
| ABC300 | 200 |
Expected results:
| Key 1 | Column A | Key 2 | Column B | NVL(A,0) - NVL(B,0)
| ABC100 | 100 | ABC100 | 100 | 0
| ABC100 | NULL | NULL | NULL | NULL
| NULL | NULL | ABC200 | 50 | -50
| ABC300 | 200 | ABC300 | 200 | 0
| ABC400 | 300 | NULL | NULL | 300
Your result set suggests that you want something like this:
SELECT t1.key1,
(CASE WHEN t1.a <> 0 THEN t1.a END) as a,
(CASE WHEN t1.a <> 0 THEN t2.key2 END) as key2,
(CASE WHEN t1.a <> 0 THEN t2.c END) as ,
(CASE WHEN t1.a <> 0 THEN COALESCE(t1.A, 0) - COALESCE(t2.B, 0) END) as diff
FROM t1 FULL JOIN
t2
ON t1.Key1 = t2.Key2;
Your description suggests that you want:
SELECT t1.key1, t1.a, t2.key2, t2.c,
COALESCE(t1.A, 0) - COALESCE(t2.B, 0) as diff
FROM (SELECT t1.*
FROM
WHERE t1.A <> 0 OR
NOT EXISTS (SELECT 1 FROM t1 tt1 WHERE tt1.key1 = t1.key1 AND tt1.key1 <> 0)
) t1 FULL JOIN
t2
ON t1.Key1 = t2.Key2;
You could use:
SELECT t1.*, t2.*, NVL(t1.A,0) - NVL(t2.B,0)
FROM tab1 t1
FULL JOIN tab2 t2
ON t1.Key1=t2.Key2

Linking Table to Itself and Getting Relational ID

I want to get accounts that have same id as other accounts and then ultimately figure out which account it s related to (see table below for example).
Table Structure
Account ID | flag | id2
123 | Y | 1
456 | N | 1
789 | N | 1
888 | Y | 2
999 | N | 2
Results I want:
Account ID | id2 | src_account_id
456 | 1 | 123
789 | 1 | 123
999 | 2 | 888
Here's the query that I have
Select account_id, id2, src_account_id
FROM table1
WHERE id2 IN (Select id2 FROM table1 WHERE flag = 'Y')
But I'm stuck with how to get src_account_id. I'm fairly sure it involves doing an inner join the table to itself, but I'm still not sure how to get the src_account_id.
You can try this. use a subquery to get flag = 'Y' result set. then self-join
SELECT t1.AccountID,t1.id2,t2.AccountID
FROM T t1 inner join (
SELECT id2,AccountID
FROM T
WHERE flag = 'Y'
) t2 on t1.id2 = t2.id2
WHERE t1.flag = 'N'
sqlfiddle
[Results]:
| AccountID | id2 | AccountID |
|-----------|-----|-----------|
| 456 | 1 | 123 |
| 789 | 1 | 123 |
| 999 | 2 | 888 |
Self join the table on id2 and flag.
SELECT t1."Account ID",
t1.id2,
t2."Account ID" src_account_id
FROM elbat t1
INNER JOIN elbat t2
ON t2.id2 = t1.id2
AND t1.flag = 'N'
AND t2.flag = 'Y';

SQL Not IN query is taking time with similar table

i have 2 tables suppose table_1 & table_2
table_1 has 56 columns and 1.2 million records
my query is like
table_1 like
RollNumber | Subject | G | Part | Status
------------------------------------------------
1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 2 | 1
1 | 2 | 1 | 1 | 1
1 | 2 | 1 | 2 | 5
1 | 3 | 1 | 1 | 0
1 | 3 | 1 | 2 | 1
2 | 1 | 2 | 1 | 1
2 | 1 | 2 | 2 | 1
2 | 2 | 2 | 1 | 1
2 | 2 | 2 | 2 | 1
2 | 3 | 2 | 1 | 1
2 | 3 | 2 | 2 | 1
3 | 1 | 2 | 1 | 1
3 | 1 | 2 | 2 | 1
3 | 2 | 2 | 1 | 1
3 | 2 | 2 | 2 | 1
3 | 3 | 2 | 1 | 0
3 | 3 | 2 | 2 | 1
i want all RollNumber (group by with 2nd and third column) from table_1 where any status is 0 but don't want students who also have status = 5(or other than 1)
i have tried this
select * from table_1 as t1
inner join table_2 as t2
on t1.column2 = t2.column2 and t1.column3 = t2.column3 and t1.column4 = t2.column4
where t1.column1 not in
(select column1 from table_1 where status = 5)
This is the inner most query of my qhole query
i have also tried EXCEPT clause
Both queries take too long to execute
Starting with SQL Server 2008 you can use count() over() to count how many total rows in a given group have a certain value.
In this case you'll want to count the number of status <> 1 per group and to only select rows that belong to a group with a count of 0.
select * from (
select * ,
count(case when status <> 1 then 1 end) over(partition by RollNumber, G) c
from table_1
) t where c = 0
You can use EXISTS in place of NOT IN. This will be faster as there will be a boolean comparison instead of string comparison.
select * from table_1 as t1
inner join table_2 as t2
on t1.column1 = t2.column1 and t1.column2 = t2.column2 and t1.column3 = t2.column3
where not EXISTS
(select 1 from table_1 where status = 5 and t1.column3 = table_1.column3)
Try to use NOT EXISTS instead of NOT IN
SELECT *
FROM table_1 AS t1
INNER JOIN table_2 AS t2
ON t1.column1 = t2.column1 AND t1.column2 = t2.column2 AND t1.column3 = t2.column3
WHERE NOT EXISTS(
SELECT 1
FROM table_1
WHERE status=5 AND column3=t1.column3
)

Overwrite NULL result in query with next NOT NULL value

I have two tables:
t1
t1.id | t1.val
----- | ------
1 | a
2 | b
3 | c
4 | d
5 | e
6 | f
7 | j
and t2
t2.id|t2.val
---- | ---
1| www
3| xxx
6| yyy
7| zzz
When I apply such sql-instruction:
SELECT t1.id, t1.val, t2.val
FROM t1
LEFT JOIN t2 ON ( t1.id = t2.id )
And result gives same table
t1.id | t1.val | t2.val
----- | ------ | ------
1 | a | www
2 | b | NULL
3 | c | xxx
4 | d | NULL
5 | e | NULL
6 | f | yyy
7 | j | zzz
Help me change the sql-instruction if I want to get result like this
t1.id | t1.val | t2.val
----- | ------ | ------
1 | a | www
2 | b | xxx
3 | c | xxx
4 | d | yyy
5 | e | yyy
6 | f | yyy
7 | j | zzz
Thanks for all!!
One method uses a correlated subquery:
select t1.*,
(select t2.val
from t2
where t2.id >= t1.id
order by t2.id
limit 1
) as t2val
from t1;
Another method uses window functions, but it is a bit more complicated:
SELECT t1.id, t1.val, t2.val
FROM (SELECT t1.id, t1.val,
MIN(t2.id) OVER (ORDER BY id DESC) as matching_id
FROM t1 LEFT JOIN
t2
ON t1.id = t2.id
) t LEFT JOIN
t2
ON t2.id = t.matching_id;
SELECT t1.id, t1.val, NVL(t2.val,LEAD(t2.val) OVER (ORDER BY t2.id))
FROM t1
LEFT JOIN t2 ON ( t1.id = t2.id )

Get counts based on a group of dates

I have two tables:
Table 1 which the fields
o_id (Fk),
dt
I have Table 2
o_id (Fk),
attr1 (Number)
My objective is count how many occurrences of attr1 occur where the value is from 0-10, 11-20, 21+. I then need to take these occurrences and Group them by dt from Table 1
To make it more clear I provide a sample output:
| Date |count(0-10) | count(11-20) | count(21+) |
-----------------------------------------------------
| 01/13 | 3 | 5 | 13 |
| 02/13 | 2 | 7 | 10 |
This is the closest I can get but it doesn't quite work:
SELECT
t1.dt,
(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 BETWEEN 0 AND 10),
(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 BETWEEN 11 AND 20),
(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 > 21),
FROM
table1 t1, table2 t2
WHERE
t1.o_id = t2.o_id
GROUP BY
t1.dt;
The results I get now:
| Date |count(0-10) | count(11-20) | count(21+) |
-----------------------------------------------------
| 01/13 | 3 | 5 | 13 |
| 02/13 | 3 | 5 | 13 |
| 02/14 | 3 | 5 | 13 |
| 02/15 | 3 | 5 | 13 |
| 02/16 | 3 | 5 | 13 |
| 02/17 | 3 | 5 | 13 |
| 02/18 | 3 | 5 | 13 |
Just keeps repeating inside the counts column.
You don't need subqueries. This will do what you seem to want:
SELECT
t1.dt,
count(CASE WHEN attr1 BETWEEN 0 AND 10 THEN 1 END),
count(CASE WHEN attr1 BETWEEN 11 AND 20 THEN 1 END),
count(CASE WHEN attr1 > 20 THEN 1 END),
FROM
table1 t1
JOIN table2 t2
ON t1.o_id = t2.o_id
GROUP BY
t1.dt
;
The COUNT() aggregate function counts the number of rows in each group for which the argument expression is not NULL. The CASE expressions that are being counted are non-null only when their (one each) WHEN condition is satisfied.
The issue is that you don't join the tables in the subqueries, but in any case this query can be done as a conditional aggregation which at least I would think look a bit better:
SELECT
t1.dt,
SUM(CASE WHEN attr1 BETWEEN 0 AND 10 THEN 1 ELSE 0 END) AS "count(0-10)",
SUM(CASE WHEN attr1 BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS "count(11-20)",
SUM(CASE WHEN attr1 > 21 THEN 1 ELSE 0 END) AS "count(21+)"
FROM
table1 t1
JOIN
table2 t2
ON
t1.o_id = t2.o_id
GROUP BY
t1.dt;
This can be done using SUM(CASE):
SELECT
t1.dt,
count(case when attr1 BETWEEN 0 AND 10 then tt2.o_id end),
count(case when attr1 BETWEEN 11 AND 20 thent t2.o_id end),
count(case when attr1 > 21 thent t2.o_id end)
FROM
table1 t1, table2 t2
WHERE
t1.o_id = t2.o_id
GROUP BY
t1.dt;
SELECT
t1.dt
,(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 BETWEEN 0 AND 10 AND t1.o_id=t2,o_id)
,(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 BETWEEN 11 AND 20 AND t1.o_id=t2,o_id)
,(select count(tt2.o_id) from table1 tt1, table2 tt2 WHERE attr1 > 21 AND t1.o_id=t2,o_id)
FROM table1 t1, table2 t2
WHERE t1.o_id = t2.o_id
GROUP BY t1.dt;