Oracle-SQL Join on Duplicate columns but avoid Duplicate rows - sql

I have table 1 like this:
id
Country
1
Germany
1
USA
1
Japan
2
France
Table 2 like this
id
Color
1
Green
2
Red
2
Yellow
Is it possible to get result like this using SQL statement?:
id
Country
Color
1
Germany
Green
1
USA
1
Japan
2
France
Red
2
Yellow
It means that, if id 1 has 3 countries and 1 color --> The result should return only 3 countries and 1 color in any order (and color can be in the same row with any country). Generally, if id 1 has m countries and n color --> The result should return only m countries and n colors ?
Thank you very much <3
Note: I'm using Oracle Database

You can number your countries and colors per ID and then use a full outer join on the ID and that number:
with cntr as
(
select id, row_number() over (partition by id order by country) as subid, country
from country
)
, clr as
(
select id, row_number() over (partition by id order by color) as subid, color
from color
)
select id, cntr.country, clr.color
from cntr full outer join clr using (id, subid)
order by id, subid nulls last;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=e74ece8cb4571d7998014f8c55bd8d7a

with src as (
select n.id,n.country,l.color,
(select count(id)-1
from countries where id=n.id) total_countries
,(
select count(id)-1
from colors where id=l.id
) total_colors
from countries n
inner join colors l
on n.id = l.id
)
select
id,
country,
lag(color, total_countries) over (partition by id order by color) color
from
src
where total_countries > 0
union all
select
id,
lag(country, total_colors) over (partition by id order by country) country
,color
from
src
where total_colors > 0
order by id, country nulls last, color nulls last
fiddle

Related

Display the total number of each catagory in each column seperately -- SQL

Where is a database like
gender
ssc_b
F
Central
F
Central
F
Other
M
Central
M
Other
I used the count and group by command but it shows:
gender
num_gender
ssc_b
num_ssc_b
F
2
Central
2
F
1
Other
1
M
1
Central
1
M
1
Other
1
I want the display the total number of each catagory in each column seperately, like
gender
num_gender
ssc_b
num_ssc_b
F
3
Central
3
M
2
Other
2
SELECT gender as key, count(*) as value
FROM <table>
GROUP BY gender
UNION ALL
SELECT ssc_b as key, count(*) as value
FROM <table>
GROUP BY ssc_b
Exactly for what you asked, the answer is
select a.gender, a.s, b.gender, b.s from
(select ROW_NUMBER() OVER() AS num_row, gender, sum(num_gender) s from t group by gender) a
outer join
(select ROW_NUMBER() OVER() AS num_row, ssc_b, sum(num_ssc_b) s from t group by ssc_b) b
on a.num_row=b.num_row
But maybe more logical would be to have the inner queries above as two separate queries.

How to select all records of n groups?

I want to select the records of the top n groups. My data looks like this:
Table 'runner':
id gid status rtime
---------------------------
100 5550 1 2016-08-19
200 5550 2 2016-08-22
300 5550 1 2016-08-30
100 6050 3 2016-09-01
200 6050 1 2016-09-02
100 6250 1 2016-09-11
200 6250 1 2016-09-15
300 6250 3 2016-09-19
Table 'static'
id description env
-------------------------------
100 something 1 somewhere 1
200 something 2 somewhere 2
300 something 3 somewhere 3
The unit id (id) is unique within the group but not unique in its column, because an instance of the group is generated regularly. The group id (gid) is assigned to every unit but will not generate on more than one instance.
Now, combining the tables and selecting everything or filter by a specific value is easy, but how do I select all records of, for example, the first two groups without directly refering to the group ids?
Expected result would be:
id gid description status rtime
--------------------------------------
300 6250 something 2 3 2016-09-19
200 6250 something 1 1 2016-09-15
100 6250 something 3 1 2016-09-11
200 6050 something 2 1 2016-09-02
100 6050 something 1 3 2016-09-01
Extra Question: When I filter for a timeframe like this:
[...]
WHERE runner.rtime BETWEEN '2016-08-25' AND '2016-09-16'
Is there a simple way of ensuring, that groups are not cut off but either appear with all their records or not at all?
You can use a ROW_NUMBER() to do this. First, create a query to rank groups:
SELECT gid, ROW_NUMBER() over (order by gid desc) as RN
FROM Runner
GROUP BY gid
Then use this as a derived table to get your other info, and use a where clause to filter to the number of groups you want to see. For instance, the below would return the top 5 groups RN <= 5:
SELECT id, R.gid, description, status, rtime
FROM (SELECT gid, ROW_NUMBER() over (order by gid desc) as RN
FROM Runner
GROUP BY gid) G
INNER JOIN Runner R on R.gid = G.gid
INNER JOIN Statis S on S.id = R.id
WHERE RN <= 5 --Change this to see more or less groups
For your second question about dates, you can do this with a subquery like so:
SELECT *
FROM Runner
WHERE gid IN (SELECT gid
FROM Runner
WHERE rtime BETWEEN '2016-08-25' AND '2016-09-16')
Hmmm. I suspect this might do what you want:
select top (1) with ties r.*
from runner r
order by min(rtime) over (partition by gid), gid;
At least, this will get the complete first group.
In any case, the idea is to include gid as a key in the order by and to use top with ties.
you can do the following
with report as(
select n.id,n.gid,m.description,n.status,n.rtime, dense_rank() over(order by gid desc) as RowNum
from #table1 n
inner join #table2 m on n.id = m.id )
select id,gid,description,status,rtime
from report
where RowNum<=2 -- <-- here n=2
order by gid desc,rtime desc
here a working demo
DENSE_RANK looks like a ideal solution here
Select * From
(
select DENSE_RANK() over (order by gid desc) as D_RN, r.*
from runner r
) A
Where D_RN = 1
No need to use ranking functions (ROW_NUMBER, DENSE_RANK etc).
SELECT r.id, gid, [description], [status], rtime
FROM runner r
INNER JOIN static s ON r.id = s.id
WHERE gid IN (
SELECT TOP 2 gid FROM runner GROUP BY gid ORDER BY gid DESC
)
ORDER BY rtime DESC;
The same using CTE:
WITH grouped
AS
(
SELECT TOP 2 gid
FROM runner GROUP BY gid ORDER BY gid DESC
)
SELECT r.id, grouped.gid, [description], [status], rtime
FROM runner r
INNER JOIN static s ON r.id = s.id
INNER JOIN grouped ON r.gid = grouped.gid
ORDER BY rtime DESC;

TSQL - Query for multiple types in multiple rows

I have a table where each address has different types, for each type a row. How to find the addresses where for the exact types i need?
Eg.
ID TypID Street
1 1 Street 1
1 2 Street 1
2 2 Street 2
3 1 Street 3
3 2 Street 3
In the above i need to find addresses which has type 1 and 2. That query result should be adresses with id 1 and 3.
Group by the id and then count the different typeids in the having clause
select id from your_table
where typeid in (1,2)
group by id
having count(distinct typeid) = 2
You can use INTERSECT for this
select id
from tbl
where typid = 1
intersect
select id
from tbl
where typid = 2
although it won't work in mysql if that happens to be the database you're using.
This can be done with a inner join like the below
select y2.*
from <your_table> Y1
JOIN <your_table> Y2
ON Y1.ID = Y2.ID
AND Y1.Type_id in (1,2)
AND Y2.ID in (1,3)

City names as column title

I have two tables.
Food Table
--------------------------
ID CityID FoodName
--------------------------
1 1 FoodA
2 1 FoodB
3 1 FoodC
4 2 FoodW
5 2 FoodX
6 2 FoodY
7 2 FoodZ
City Table
--------------------------
ID CityName
--------------------------
1 Memphis
2 Nashville
3 Chattanooga
So How can I use CityName s as Column title and list the food in that city.
--------------------------------------
Memphis Nashville Chattanooga
--------------------------------------
FoodA FoodW
FoodB FoodX
FoodC FoodY
FoodZ
I'm pretty sure on that I have to use pivot but I couldn't find a good solution yet.
This is what I've achieved so far.
SELECT *
FROM (
SELECT *
FROM Food F
INNER JOIN City C ON C.ID = F.CityID
) DataTable D
PIVOT(F.FoodName FOR C.CityName IN (
[Memphis]
,[Nashville]
,[Chattanooga]
)) PivotTable
you can use this query to get your output. Actually you did some mistakes to setup the pivot query.
select Memphis,Nashville,Chattanooga
from
(
select f.ID,c.CityName,f.FoodName
from Food f
inner join City c
on f.CityID=c.id
)result
pivot
(
max(FoodName)
for CityName in(Memphis,Nashville,Chattanooga)
) as pvt
The PIVOT operator uses the columns from the data table that are not in the PIVOT definition as GROUP anchor.
That mean that two values will be in the same row of a PIVOT table when they have the same value in the columns of data table that are neither the aggregated one or the pivoted one.
The OP data don't have this value so a new partitioned id is generated.
SELECT Memphis, Nashville, Chattanooga
FROM (SELECT c.CityName, f.FoodName
, FoodID = Row_Number() OVER (PARTITION BY c.ID ORDER BY FoodName)
FROM Food f
INNER JOIN City c ON f.CityID = c.id) d
PIVOT
(MAX(FoodName) FOR CityName IN (Memphis,Nashville,Chattanooga)) pvt

Within the same group find and exclude records that have the same parent ID for certain types

I have a table like following:
GroupID ParentID Type
1 ABC IND
1 ABC IND
1 CDE ORD
1 EFG STD
2 ZZZ IND
2 ZZZ IND
2 ZZZ IND
3 YYY COR
3 YYY COR
I need to exclude those records that are in the same group, having the same parent ID and the type is IND or COR. But I need to keep those groups that have different parent ID and the type is not IND or COR.
So the result I want to get would be the following:
GroupID ParentID Type
1 ABC IND
1 ABC IND
1 CDE ORD
1 EFG STD
Somehow I am thinking to use
Rank () over(partition by GroupID order by ParentID), but it won't give me the results that I want.
Any thoughts? PS: This table has 5 Million+ records. Looking for the effective way to deal with it.
Thanks
The following gives you a list of the groupIDs you want to exclude
SELECT GroupID
FROM
(
SELECT GroupID,
COUNT(DISTINCT ParentID) AS PCount, COUNT(DISTINCT TypeCode) as TCount,
MAX(TypeCode) AS tCode
FROM tablename
GROUP BY GroupID
) t
WHERE PCount = 1 AND TCount = 1
AND (tCode = 'IND' OR tCode = 'COR')
Now select everything else
SELECT *
FROM tableName
WHERE GroupID not in (
SELECT GroupID
FROM
(
SELECT GroupID,
COUNT(DISTINCT ParentID) AS PCount, COUNT(DISTINCT TypeCode) as TCount,
MAX(TypeCode) AS tCode
FROM tablename
GROUP BY GroupID
) t
WHERE PCount = 1 AND TCount = 1
AND (tCode = 'IND' OR tCode = 'COR')
)
Test with fiddle --> http://sqlfiddle.com/#!3/f1d4f/15/0
How is
1 ABC IND
in result set? here type is IND and you mentioned the result set should not have type IND or COR?