How to combine data from 2 tables -- which join, what conditions? - sql

Consider the following 2 tables.
TableDE
ID country key1 key2
------------------------
1 US 1 null
1 US 1 null
1 US 1 null
2 US null null
3 US 1 1
4 DE 1 1
5 DE null null
5 DE null null
TableUS
ID key1 key2
--------------
1 null null
2 null 1
4 1 1
8 null 1
2 null 1
2 null 1
9 1 null
I need a distinct overview of all IDs, combining data from both tables:
ID inTableDe country DEkey1 DEkey2 inTableUS USkey1 USKey2
-----------------------------------------------------------------
1 1 US 1 0 1 0 0
2 1 US 0 0 1 0 1
3 1 US 1 1 0 0 0
4 1 DE 1 1 1 1 1
5 1 DE 0 0 0 0 0
8 0 0 0 1 1 0 1
9 0 0 0 1 1 1 0
I hope it speaks for itself:
ID 8 and ID 9 have 0 in the first column bc they aren't in tableDE
ID 8 and ID 9 have 0 in the country column bc this field doesn't exist in tableUS
ID 3 has 0 in inTableUS bc it only exists in tableDE
the key values are copied from the original tables
an ID is not unique: it can appear many times in both tables. However: the values for key1 and key2 will always be the same for each ID within the same table.
I have been messing for hours now with this; I have this now:
select de.[ID],
de.[country],
case when (de.[ID] in (select distinct [ID] from [tableDE]) then 1 else 0 end as [inTableDE],
case when (de.[ID] in (select distinct [ID] from [tableUS]) then 1 else 0 end as [inTableUS],
de.[key1] as [DEKey1],
de.[key2] as [DEKey2],
us.[key1] as [USKey1],
us.[key2] as [USKey2],
from dbo.[tableDE] de
full outer join dbo.[tableUS] us on de.[ID] = us.[ID]
where de.[country] = 'US'
and (de.[key1] = 1 or de.[key2] = 1 or us.[key1] = 1 or us.[key2] = 1)
group by de.[ID], us.[ID]
But this keeps giving me only values that are in both tables.
What am I doing wrong?

You sem to want aggregation on top of the full join:
select
coalesce(de.id, us.id) as id,
case when de.id is null then 0 else 1 end as intablede,
max(de.country) as country,
coalesce(max(de.key1), 0) as dekey1,
coalesce(max(de.key2), 0) as dekey2,
case when us.id is null then 0 else 1 end as intableus,
coalesce(max(us.key1), 0) as uskey1,
coalesce(max(us.key2), 0) as uskey2
from dbo.tablede de
full join dbo.tableus us on de.id = us.id
group by de.id, us.id
order by id
Demo on DB Fiddle:
id | intablede | country | dekey1 | dekey2 | intableus | uskey1 | uskey2
-: | --------: | :------ | -----: | -----: | --------: | -----: | -----:
1 | 1 | US | 1 | 0 | 1 | 0 | 0
2 | 1 | US | 0 | 0 | 1 | 0 | 1
3 | 1 | US | 1 | 1 | 0 | 0 | 0
4 | 1 | DE | 1 | 1 | 1 | 1 | 1
5 | 1 | DE | 0 | 0 | 0 | 0 | 0
8 | 0 | null | 0 | 0 | 1 | 0 | 1
9 | 0 | null | 0 | 0 | 1 | 1 | 0

Related

PostgresQL: Finding records that do not exist in query

Below, I have a query for a rankings table of players in a leaderboard.
Player information (including pseudonym) is stored in the player table, while rankings for each "matchday" (identified by edition_id) are stored in players_rankings as the competition is lineal (there's no points system, so rankings can't be computed mathematically). Information for each fixture is stored in set (sw denotes set wins, while sl denotes set losses).
SELECT
players_rankings.rank, players_rankings.change, player.pseudonym,
SUM(tot.sw) AS sw,
SUM(tot.sl) AS sl
FROM players_rankings, player, (
SELECT
player1_id AS player_id,
CASE
WHEN score1 > score2 THEN 1 ELSE 0
END AS sw,
CASE
WHEN score1 < score2 THEN 1 ELSE 0
END AS sl
FROM set WHERE edition_id = 1
UNION ALL
SELECT
player2_id,
CASE
WHEN score1 < score2 THEN 1 ELSE 0
END,
CASE
WHEN score1 > score2 THEN 1 ELSE 0
END
FROM set WHERE edition_id = 1
) AS tot
WHERE
players_rankings.edition_id = 1 AND
tot.player_id = players_rankings.player_id AND
players_rankings.player_id = player.id
GROUP BY 1, 2, 3
UNION
SELECT players_rankings.rank, players_rankings.change, player.pseudonym, 0, 0
FROM players_rankings, player
WHERE
players_rankings.edition_id = 1 AND
players_rankings.player_id = player.id
ORDER BY 1;
Which produces the following result:
-----+--------+---------------+----+----+
rank | change | pseudonym | sw | sl |
-----+--------+---------------+----+----+
1 | 0 | Player One | 1 | 0 |
-----+--------+---------------+----+----+
1 | 0 | Player One | 0 | 0 |
-----+--------+---------------+----+----+
2 | 0 | Player Two | 0 | 0 |
-----+--------+---------------+----+----+
3 | 2 | Player Three | 1 | 0 |
-----+--------+---------------+----+----+
3 | 2 | Player Three | 0 | 0 |
-----+--------+---------------+----+----+
4 | -1 | Player Four | 0 | 1 |
-----+--------+---------------+----+----+
4 | -1 | Player Four | 0 | 0 |
-----+--------+---------------+----+----+
5 | -1 | Player Five | 1 | 0 |
-----+--------+---------------+----+----+
5 | -1 | Player Five | 0 | 0 |
-----+--------+---------------+----+----+
6 | 3 | Player Six | 0 | 0 |
-----+--------+---------------+----+----+
6 | 3 | Player Six | 1 | 0 |
-----+--------+---------------+----+----+
7 | -1 | Player Seven | 0 | 0 |
-----+--------+---------------+----+----+
7 | -1 | Player Seven | 0 | 1 |
-----+--------+---------------+----+----+
8 | -1 | Player Eight | 0 | 0 |
-----+--------+---------------+----+----+
8 | -1 | Player Eight | 0 | 1 |
-----+--------+---------------+----+----+
9 | -1 | Player Nine | 0 | 0 |
-----+--------+---------------+----+----+
10 | 0 | Player Ten | 0 | 1 |
-----+--------+---------------+----+----+
10 | 0 | Player Ten | 0 | 0 |
-----+--------+---------------+----+----+
11 | 0 | Player Eleven | 0 | 0 |
-----+--------+---------------+----+----+
12 | 0 | Player Twelve | 0 | 0 |
-----+--------+---------------+----+----+
My goal with the query after the UNION was to get only registered players that didn't feature in the first "matchday" (players_rankings.edition_id = 1, i.e. players Two, Nine, Eleven, and Twelve), but I hit a brick wall trying different methods to achieve that, including different JOINs. As such, I went back to the drawing board and used the aforementioned query to start again with the duplicate values as shown above. Below is the desired result:
-----+--------+---------------+----+----+
rank | change | pseudonym | sw | sl |
-----+--------+---------------+----+----+
1 | 0 | Player One | 1 | 0 |
-----+--------+---------------+----+----+
2 | 0 | Player Two | 0 | 0 |
-----+--------+---------------+----+----+
3 | 2 | Player Three | 1 | 0 |
-----+--------+---------------+----+----+
4 | -1 | Player Four | 0 | 1 |
-----+--------+---------------+----+----+
5 | -1 | Player Five | 1 | 0 |
-----+--------+---------------+----+----+
6 | 3 | Player Six | 1 | 0 |
-----+--------+---------------+----+----+
7 | -1 | Player Seven | 0 | 1 |
-----+--------+---------------+----+----+
8 | -1 | Player Eight | 0 | 1 |
-----+--------+---------------+----+----+
9 | -1 | Player Nine | 0 | 0 |
-----+--------+---------------+----+----+
10 | 0 | Player Ten | 0 | 1 |
-----+--------+---------------+----+----+
11 | 0 | Player Eleven | 0 | 0 |
-----+--------+---------------+----+----+
12 | 0 | Player Twelve | 0 | 0 |
-----+--------+---------------+----+----+
How should I go about achieving this?
Use window function ROW_NUMBER() and partiton by rank and sort with case statement.
Using the Row_Number function, group the rows that have the same rank, and then, based on the fact that the row has the condition sw = 1 OR sl = 1, the value of one is included in the sort, otherwise the value 0 is then sorted in descending order.
In fact, the Row_Number function numbers the rows based on the same rank, and in the main query, the rows that are numbered number one are fetched.
SELECT rank,change,pseudonym,sw,sl
FROM
(SELECT *,
ROW_NUMBER() OVER(PARTITION BY rank ORDER BY CASE WHEN sw = 1 OR sl = 1 THEN 1 ELSE 0 END DESC) AS num
FROM
(SELECT
players_rankings.rank, players_rankings.change, player.pseudonym,
SUM(tot.sw) AS sw,
SUM(tot.sl) AS sl
FROM players_rankings, player, (
SELECT
player1_id AS player_id,
CASE
WHEN score1 > score2 THEN 1 ELSE 0
END AS sw,
CASE
WHEN score1 < score2 THEN 1 ELSE 0
END AS sl
FROM set WHERE edition_id = 1
UNION ALL
SELECT
player2_id,
CASE
WHEN score1 < score2 THEN 1 ELSE 0
END,
CASE
WHEN score1 > score2 THEN 1 ELSE 0
END
FROM set WHERE edition_id = 1
) AS tot
WHERE
players_rankings.edition_id = 1 AND
tot.player_id = players_rankings.player_id AND
players_rankings.player_id = player.id
GROUP BY 1, 2, 3
UNION
SELECT players_rankings.rank, players_rankings.change, player.pseudonym, 0, 0
FROM players_rankings, player
WHERE
players_rankings.edition_id = 1 AND
players_rankings.player_id = player.id) T) T
WHERE num = 1
ORDER BY 1;
Demo in db<>fiddle
Caveat: I haven't finished my morning coffee yet... If I understand your question correctly the following (un-tested) approach could work:
WITH pr AS (
SELECT players_rankings.player_id,
players_rankings.rank,
players_rankings.change,
player.pseudonym
FROM players_rankings
JOIN player
ON ( players_rankings.player_id = player.id )
WHERE players_rankings.edition_id = 1
),
tot AS (
SELECT t.player_id,
sum ( t.sw ) AS sw,
sum ( t.sl ) AS sl
FROM (
SELECT player1_id AS player_id,
CASE
WHEN score1 > score2 THEN 1
ELSE 0
END AS sw,
CASE
WHEN score1 < score2 THEN 1
ELSE 0
END AS sl
FROM SET
WHERE edition_id = 1
UNION ALL
SELECT player2_id,
CASE
WHEN score1 < score2 THEN 1
ELSE 0
END,
CASE
WHEN score1 > score2 THEN 1
ELSE 0
END
FROM SET
WHERE edition_id = 1
) AS t
GROUP BY t.player_id
)
SELECT pr.rank,
pr.change,
pr.pseudonym,
0 AS sw,
0 AS sl
FROM pr
FULL OUTER JOIN tot
ON ( pr.player_id = tot.player_id )
WHERE tot.pseudonym IS NULL
ORDER BY 1 ;
edit fix columns in full outer join

SQL sum total each column in last row

I wish SQL for SUM each column(IPO and UOR) in TOTAL in second last. And GRAND TOTAL(Sum IPO + UOR) in the last one. Thank you so much
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
TOTAL (NOS) 25 6
-------------------------
GRAND TOTAL (NOS) 31
Here is my code, :
SELECT
SUM(CASE WHEN IPOType = 'IPO' THEN 1 ELSE 0 END) as IPO,
SUM(CASE WHEN IPOType = 'UOR' THEN 1 ELSE 0 END) as UOR
FROM IPO2018
GROUP BY OriProjNo
it can show like this
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
Generally speaking, you want to leave totals and sub-totals to whatever tool you are presenting your data in, as they will be able to handle the formatting with significantly more ease. In addition, your desired output does not have the same number of columns (Grand Total row only has one numeric) so even if you did shoehorn this in to the same dataset, the column headings wouldn't make sense.
That said, you can return group totals via the with rollup statement. This will provide an additional row with the aggregate totals for the group. Where there is more than one group in your data, you will get a sub-total row for each group and a total row for the entire dataset:
declare #t table(c nvarchar(10),t nvarchar(3));
insert into #t values ('D173','IPO'),('D176','IPO'),('D176','IPO'),('D176','IPO'),('D184','IPO'),('D184','UOR'),('D185B','IPO'),('D187','IPO'),('D187','UOR'),('D187','UOR'),('F042','IPO'),('F042','IPO'),('F042','IPO'),('TTPMC','IPO'),('TTPMC','IPO'),('Z00204','IPO'),('ML004','UOR'),('ML004','UOR'),('ML004','UOR'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO');
select row_number() over (order by grouping(c),c) as n
,case when grouping(c) = 1 then 'TOTAL (NOS)' else c end as c
,sum(case when t = 'IPO' then 1 else 0 end) as IPO
,sum(case when t = 'UOR' then 1 else 0 end) as UOR
from #t
group by c
with rollup
order by grouping(c)
,c;
Output:
+----+-------------+-----+-----+
| n | c | IPO | UOR |
+----+-------------+-----+-----+
| 1 | D173 | 1 | 0 |
| 2 | D176 | 3 | 0 |
| 3 | D184 | 1 | 1 |
| 4 | D185B | 1 | 0 |
| 5 | D187 | 1 | 2 |
| 6 | F042 | 3 | 0 |
| 7 | ML004 | 12 | 3 |
| 8 | TTPMC | 2 | 0 |
| 9 | Z00204 | 1 | 0 |
| 10 | TOTAL (NOS) | 25 | 6 |
+----+-------------+-----+-----+

Toggle bit for a series of rows based on single row in SQLite

I have a table containing rows which are either 'headers' or 'normal', non-header entries. This is tracked by an INTEGER affinity column IsHeader.
Likewise, I have a column tracking if the row is 'Active'.
With a table 'Entries', and another column 'MCL_Row' used to find relevant rows, I can toggle the value of 'Active' using
UPDATE Entries SET(Active) =
(SELECT (~(Active&1))&(Active|1) WHERE MCL_Row = <target>)
WHERE MCL_Row = <target>;
This works, but if I want to toggle an entire group on or off based on the header, I can't use
UPDATE Entries SET(Active) =
(SELECT (~(Active&1))&(Active|1) WHERE S_Type = <typenum> AND IsHeader=1)
WHERE S_Type = <typenum>;
because here, the SELECT subquery returns the one value I want, but multiple rows are updated. As a result, the first row gets the correct result, and subsequent rows satisfying the WHERE S_Type = <typenum> clause are updated with a NULL value.
How can I use the value returned by this subclause to set the values (identically) of multiple rows used by the UPDATE statement?
Edit: Perhaps the question was a little unclear originally, so adding some example before/after data.
Before:
MCL_Row S_Type Active IsHeader
1 1 1 1
2 1 1 0
3 1 0 0
4 2 1 1
5 2 1 0
6 2 1 0
After setting S_Type=1 active via header:
MCL_Row S_Type Active IsHeader
1 1 1 1
2 1 1 0
3 1 >1< 0
4 2 1 1
5 2 1 0
6 2 1 0
After setting S_Type=1 inactive via header:
MCL_Row S_Type Active IsHeader
1 1 >0< 1
2 1 >0< 0
3 1 0 0
4 2 1 1
5 2 1 0
6 2 1 0
1st query
UPDATE Entries
SET Active = 1-Active
WHERE MCL_Row = <target>
;
2nd query
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.S_Type = Entries.S_Type
and h.IsHeader = 1
)
WHERE S_Type = <typenum>
Demo
create table Entries (MCL_Row int,S_Type int,IsHeader int,active int);
insert into Entries (MCL_Row,S_Type,IsHeader,active) values
(1,123,1,1)
,(2,123,0,0)
,(3,123,0,0)
,(4,123,0,1)
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 1 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 1 |
+---------+--------+----------+--------+
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.IsHeader = 1
and h.S_Type = Entries.S_Type
)
WHERE S_Type = 123
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 0 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 1 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 1 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 1 |
+---------+--------+----------+--------+
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.IsHeader = 1
and h.S_Type = Entries.S_Type
)
WHERE S_Type = 123
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 1 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 0 |
+---------+--------+----------+--------+

SQL Server - Compare values from the same table

In SQL Server, I have one table with following data (tblUserSettings):
| CountryID | CityID | UserType | Value1 | Value2 | Value3 |
| 9 | 3 | 1 | 5 | 5 | 5 |
| 9 | 3 | 2 | NULL | NULL | NULL |
| 9 | 3 | 3 | 5 | 5 | 5 |
| 9 | 3 | 4 | 5 | 5 | 5 |
| 9 | 20 | 1 | 5 | 5 | 5 |
| 9 | 20 | 2 | NULL | NULL | NULL |
| 9 | 20 | 3 | 5 | 5 | 5 |
| 9 | 20 | 4 | 0 | 0 | 0 |
I need to compare all the values for all UserTypes from CityID = 20 with all the values for corresponding UserTypes from CityID = 3. The CountryID = 9. The columns to compare are: Value1, Value2, Value3.
I just need to know if all of them are matched to each other or not. I tried to do something as follows:
SELECT CASE WHEN ISNULL(t1.Value1, 0) = ISNULL(t2.Value1, 0) THEN 1 ELSE 0 END AS Match1,
CASE WHEN ISNULL(t1.Value2, 0) = ISNULL(t2.Value2, 0) THEN 1 ELSE 0 END AS Match2,
CASE WHEN ISNULL(t1.Value3, 0) = ISNULL(t2.Value3, 0) THEN 1 ELSE 0 END AS Match3
FROM tblUserSettings t1
INNER JOIN tblUserSettings t2 ON t1.CountryID = t2.CountryID
AND t1.UserType = t2.UserType
AND t1.CityID = 3
AND t2.CityID = 20
WHERE t1.CountryID = 9
And it gives me following result which I have to process further to define if everything matches or not.
| Match1 | Match2 | Match3 |
| 1 | 1 | 1 |
| 1 | 1 | 1 |
| 1 | 1 | 1 |
| 0 | 0 | 0 |
Can I do this in a way to have only one column and row in output - just receive either 1 for all the matches or 0 if at least one doesn't match?
If you are looking to get only one column with 1 when all the values match and 0 if atleast one doesn't, use,
SELECT
CASE WHEN ISNULL(t1.Value1, 0) = ISNULL(t2.Value1, 0)
AND ISNULL(t1.Value2, 0) = ISNULL(t2.Value2, 0)
AND ISNULL(t1.Value3, 0) = ISNULL(t2.Value3, 0)
THEN 1 ELSE 0 END AS Match
FROM tblUserSettings t1
INNER JOIN tblUserSettings t2 ON t1.CountryID = t2.CountryID
AND t1.UserType = t2.UserType
AND t1.CityID = 3
AND t2.CityID = 20
WHERE t1.CountryID = 9
If you are looking to compare all cities rather than just two you should be able to do this by grouping rather than joining.
Something like:
SELECT
CASE WHEN
max(Value1)-min(Value1) = 0
AND max(Value2)-min(Value2) = 0
AND max(Value3)-min(Value3) = 0
THEN 1 ELSE 0 AS Match
FROM tblUserSettings
GROUP BY CountryID,UserType

issue with joins ins my query, how to populate null values as zero

My query is to fetch data for last 5 weeks.
select z.week,
sum(case when i.severity=1 then 1 else 0 end) as 1
sum(case when i.severity=2 then 1 else 0 end) as 2
sum(case when i.severity=3 then 1 else 0 end) as 3
sum(case when i.severity=4 then 1 else 0 end) as 4
from instance as i
and left outer join year as z on convert(varchar(10),z.date,101)=convert(varchar(10),i.created,101)
and left outer join year as z on convert(varchar(10),z.date,101)=convert(varchar(10),i.closed,101)
where i.group in '%Teams%'
and z.year=2013
and z.week<=6 and z.week>1
here there are few weeks in my instance table, where there will be not even an single row. so here im not getting null or zero... instead the entire row is not at all prompting.
my present output.
week | 1 | 2 | 3 | 4
---------------------
2 | 0 | 1 | 8 | 5
3 | 2 | 3 | 4 | 9
5 | 1 | 0 | 0 | 0
but i need output like the below...
week | 1 | 2 | 3 | 4
---------------------
2 | 0 | 1 | 8 | 5
3 | 2 | 3 | 4 | 9
4 | 0 | 0 | 0 | 0
5 | 1 | 0 | 0 | 0
6 | 0 | 0 | 0 | 0
How to get the desired outputi n sql
try this
select z.week,
sum(case when i.severity=1 then 1 else 0 end) as 1
sum(case when i.severity=2 then 1 else 0 end) as 2
sum(case when i.severity=3 then 1 else 0 end) as 3
sum(case when i.severity=4 then 1 else 0 end) as 4
from year as z
left outer join instance as i on
convert(varchar(10),z.date,101)=convert(varchar(10),i.created,101)
and convert(varchar(10),z.date,101)=convert(varchar(10),i.closed,101)
where (i.group is null or i.group in '%Teams%')
and z.year=2013
and z.week<=6 and z.week>1
I'm not sure how the query works where you alias year twice to z. But, assuming that's not a problem, you can change the LEFT OUTER JOIN to RIGHT OUTER JOIN. Or, if you don't like the RIGHT OUTER JOIN, rework the SELECT so that the FROM clause references the year table.