How to not lose records in full join - sql

Let's say I have two tables; table A and table shown below:
A
Color ID
Blue 1
Green 2
Red 3
B
Color ID
Blue 1
Brown 2
Red 3
If I were to attempt to join them using a full join, the result would depend on which table I use in the select statement. For example the following query would produce the following result
select A.color, count(*)
from A
full join B on a.color = B.color
group by 1
order by 1
color count
Blue 1
Green 1
Red 1
1
If I decided to use B.color in the select statement instead of A.color, I would get the result below:
color count
Blue 1
Brown 1
Red 1
1
How would I get the resultset to include all values for color. I know I could accomplish using unionall, and I could use a case statement in the select statement to use one when the other is null, but is there another cleaner way to accomplish this?
Thanks,
Ben

Use coalesce to pick up the value from the other table in case the value exists in one table and not the other.
select coalesce(A.color,B.color) as color, count(*)
from A
full join B on a.color = B.color
group by 1
order by 1

Related

How to filter out all records that have duplicates in SQL?

Trying to get this result from a table with duplicates
red
red
red
blue
green
to
blue
green
Totally omitting all the records that has duplicates and only bringing in the unique records
Use GROUP BY and HAVING clause...
select color
from table1
group by color
having count(color) = 1
If you need more than just the colours.
select *
from paintmess
where colour in (
select colour
from paintmess
group by colour
having count(*)=1
);
id
colour
4
blue
5
green
db<>fiddle here

Select rows that belong to ONLY one category among many

Let's say there is this table colors
id
source
1
red
1
green
1
orange
2
red
2
red
3
black
3
green
4
red
5
green
What I want is the list of all id that have as only source the value 'red', so 2 and 4.
To be clear
select distinct id from colors where source = 'red'
would give 1,2 and 4, where 1 has 'green' and 'orange' in addition to 'red', so no.
Here the SQL to create the table
create temp table colors as
select *
from (values (1, 'red'),(1, 'green'),(1, 'orange'),(2,'red'),(2, 'red'),(3, 'black'),(3,'green'),(4,'red'),(5,'green'))
as t (id,source)
How to query this?
Use aggregation:
select id
from t
group by id
having count(*) filter (where color = 'red') = count(*);
I managed to find a query, but it seems so complicated and unpractical:
with colors_sint as (
--This is the list of distinct sources for every id
select id, source
from colors
group by id, source
), only_red as (
--Here I check which id has only one distinct source
select id, count(*) as n_sources
from colors_sint
group by id
having count(*) = 1
)
--among those id that have only one source, I take only those that have only red as source
select id
from colors_sint
where id in (select id from only_red)
and "source" = 'red'
Maybe someone has better ideas?

Select one distinct row based on a case statement applied to a column

I'm unable to figure out a sql query (using MS Sql Server). I'm trying to retrieve a single row from a dataset in which an item with one id can have more than one row. The part that is throwing me off is that the correct row should be based on a "hierarchy". I have trying to throw a case statement at the problem.
Some sample data:
Id Class Date
100 Red 2012-12-12
100 Blue 2012-12-31
200 Red 2012-10-31
300 Green 2012-04-04
300 Blue 2011-09-01
I want to return a single row based on the value of Class.
Case When Red Then
Date
Case When Blue Then
Date
Case When Green Then
Date
Else
''
My final dataset should look like this:
Id Class Date
100 Red 2012-12-12
200 Red 2012-10-31
300 Blue 2011-09-01
So, if one of the duplicate rows has a value of Red, use the date from that row first. Then blue, then green.
Been working on this one all day, playing around with subqueries, group bys, havings, case statements, derived tables. I'm quite rusty on my sql skills, as it's been a while.
Any hints on the direction I should take?
You can try this
;WITH cte AS
(
SELECT Id, Class, Date,
row_number() OVER (PARTITION BY Id
ORDER BY CASE Class
WHEN 'Red' THEN 1
WHEN 'Blue' THEN 2
WHEN 'Green' THEN 3
ELSE 4 END) as rn
FROM MyTable
)
SELECT Id, Class, Date
FROM cte
WHERE rn = 1
Try:
select id, class, date
from TABLE
where class = COLOR
and date = (select min(date) from TABLE where class = COLOR)
I like #bobs query and it'll work well on a TSQL database.
This is just another way of doing the same thing that may be a little more portable to other SQL databases that have common table expressions but not the same row number syntax;
;WITH cte AS
(SELECT *, CASE Class WHEN 'Red' THEN 1 WHEN 'Blue' THEN 2
WHEN 'Green' THEN 3 ELSE 4 END c FROM myTable)
SELECT b1.Id, b1.Class, b1.Date
FROM cte b1
LEFT JOIN cte b2
ON b1.Id = b2.Id AND b1.c > b2.c
WHERE b2.Class IS NULL
An SQLfiddle to test with.

How do I get multiple rows when value > 1 [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Select Records multiple times from table
I want to have my query return (multiple) rows for the value of TABLE_B.QTY.
TABLE A
SALESNR ITEMNR LINENR
100 B2001 1
101 B2002 2
102 A1021 3
TABLE B
LINENR COLOR QTY
1 WHITE 3
2 BLACK 1
3 BROWN 8
For instance, with the following query:
SELECT TABLE_A.SALESNR, TABLE_A.ITEMNR, TABLE_B.COLOR, TABLE_B.QTY
FROM TABLE_A INNER JOIN TABLE_B ON TABLE_B.LINENR = TABLE_A.LINENR
I get:
100 B2001 White 3
What I need is:
100 B2001 White 3
100 B2001 White 3
100 B2001 White 3
Is there a way to do this?
Can't think of the right keywords to Google this...
Thnx,
Mike
This will work as long as QTY is less than 2047
SELECT TABLE_A.SALESNR, TABLE_A.ITEMNR, TABLE_B.COLOR, TABLE_B.QTY
FROM TABLE_A
INNER JOIN TABLE_B ON TABLE_B.LINENR = TABLE_A.LINENR
INNER JOIN master..spt_values ON type = 'P' AND number < TABLE_B.QTY
use this if QTY exceeds 2047:
;WITH a AS
(
SELECT TABLE_A.SALESNR, TABLE_A.ITEMNR, TABLE_B.COLOR, TABLE_B.QTY, 1 row
FROM TABLE_A
INNER JOIN TABLE_B ON TABLE_B.LINENR = TABLE_A.LINENR
WHERE QTY > 0
union all
SELECT SALESNR, ITEMNR, COLOR, QTY, row+1
FROM a
WHERE QTY > row
)
SELECT SALESNR, ITEMNR, COLOR, QTY from a
OPTION (MAXRECURSION 0)
The cross join won't do it if you have one row in each table, which I interpret it as. I would suggest, if possible, to re-design your data model to solve this - or loop in code where you use this data.
You can loop in the T-SQL if absolutely needed.
regards, Olle
Can't seem to comment other peoples posts, just wanted to say, nice solution, to t-clausen.dk!

How to get a proper count in sql server when retrieving a lot of fields?

Here is my scenario,
I have query that returns a lot of fields. One of the fields is called ID and I want to group by ID and show a count in descending order. However, since I am bringing back more fields, it becomes harder to show a true count because I have to group by those other fields. Here is an example of what I am trying to do. If I just have 2 fields (ID, color) and I group by color, I may end up with something like this:
ID COLOR COUNT
== ===== =====
2 red 10
3 blue 5
4 green 24
Lets say I add another field which is actually the same person, but they have a different spelling of their name which throws the count off, so I might have something like this:
ID COLOR NAME COUNT
== ===== ====== =====
2 Red Jim 5
2 Red Jimmy 5
3 Red Bob 3
3 Red Robert 2
4 Red Johnny 12
4 Red John 12
I want to be able to bring back ID, Color, Name, and Count, but display the counts like in the first table. Is there a way to do this using the ID?
If you want a single result set, you would have to omit the name, as in your first post
SELECT Id, Color, COUNT(*)
FROM YourTable
GROUP By Id, Color
Now, you could get your desired functionality with a subquery, although not elegant
SELECT Id, Color Name, (SELECT COUNT(*)
FROM YourTable
Where Id = O.Id
AND Color = O.Color
) AS "Count"
FROM YourTable O
GROUP BY Id, Color, Name
This should work as you desire
Try this:-
SELECT DISTINCT a.ID, a.Color, a.Name, b.Count
FROM yourTable
INNER JOIN (
SELECT ID, Color, Count(1) [Count] FROM yourTable
GROUP BY ID, Color
) b ON a.ID = b.ID, a.Color = b.Color
ORDER BY [Count] DESC
Try doing a sub query to get the count.
-- MarkusQ