I'm trying to figure out how to get the intersection of a dynamic input set. Here is a very simplified example.
company_status table:
COMPANY | STATUS
----------------
Big | 1
Notused | 0
Small | 1
company_country table:
COMPANY | COUNTRY
-----------------
Big | CA
Big | US
Notused | CA
Notused | FR
Small | US
Small | IT
What I want is the intersection of the countries for only certain companies.
If I select only companies where status = 1, here is my expected output:
US
If I select only companies where status = 0, here is my expected output:
CA
FR
Taking the company_status table out of the equation, this is what I need:
select country from company_status where company = 'Big'
intersect
-- ... (here is where the dynamic part comes in)
intersect
select country from company_status where company = 'Small';
But how do I add company_status into this?
If I understand correct so you want only these countries, which fulfil in every row of company_status the condition status = 1 or status = 0.
If so you could count, how many appearances are to be found in company_status and use this in the having- clause. But you of course have to put the same condition into the where- clause of the join.
WITH
company_status as (
select 'BIG' COMPANY, 1 STATUS from dual union all
select 'NOTUSED' COMPANY, 0 STATUS from dual union all
select 'SMALL' COMPANY, 1 STATUS from dual
),
company_country as (
select 'BIG' COMPANY, 'CA' COUNTRY from dual union all
select 'BIG' COMPANY, 'US' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'CA' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'FR' COUNTRY from dual union all
select 'SMALL' COMPANY, 'US' COUNTRY from dual union all
select 'SMALL' COMPANY, 'IT' COUNTRY from dual
)
select cc.country
from company_country cc join
company_status cs
on cc.company = cs.company
where cs.status = 0
group by cc.country
having count(*) = (SELECT COUNT(*) FROM company_status where status = 0);
(the with- clause only gives your rows, the rest of the question should work on your example)
But you can use with- clause to determine, which Status you want at only one place (third with- clause, would be your first):
WITH
company_status as (
select 'BIG' COMPANY, 1 STATUS from dual union all
select 'NOTUSED' COMPANY, 0 STATUS from dual union all
select 'SMALL' COMPANY, 1 STATUS from dual
),
company_country as (
select 'BIG' COMPANY, 'CA' COUNTRY from dual union all
select 'BIG' COMPANY, 'US' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'CA' COUNTRY from dual union all
select 'NOTUSED' COMPANY, 'FR' COUNTRY from dual union all
select 'SMALL' COMPANY, 'US' COUNTRY from dual union all
select 'SMALL' COMPANY, 'IT' COUNTRY from dual
),
wished_status as (select 0 wished_status from dual)
select cc.country
from company_country cc
join company_status cs on cc.company = cs.company
JOIN wished_status s on cs.status = s.wished_status
group by cc.country
having count(*) = (SELECT COUNT(*) FROM company_status cs join wished_status s on cs.status = s.wished_status);
so you only had to Change between 0 and 1 (or whatever you need) in wished_status
The basic query is:
select cc.country
from company_country cc join
company_status cs
on cc.company = cs.company
group by cc.country
Then use a having clause for your filtering. I think the two filters are:
having min(status) = max(status) and min(status) = 1
having sum(case when status = 0 then 1 else 0 end) > 0
These correspond to the result sets specified in your example.
Related
with t1 as
(select distinct oh.games,oh.noc,region as countrys from olympics_history oh inner join olympics_history_noc_regions hnr
on hnr.noc = oh.noc order by games),
t2 as
(select games,noc,count(medal) as gold_medals from olympics_history
where medal like '%Gold%'
group by noc,games
order by games),
t3 as
(select games,noc,count(medal) as Silver_medals from olympics_history
where medal like '%Silver%'
group by noc,games
order by games),
t4 as
(select games,noc,count(medal) as Bronze_medals from olympics_history
where medal like '%Bronze%'
group by noc,games
order by games),
t5 as
(select t1.games,countrys,gold_medals,Silver_medals,Bronze_medals
from t1 inner join t2 on (t1.noc = t2.noc and t1.games = t2.games)
inner join t3 on (t2.noc = t3.noc and t2.games = t3.games)
inner join t4 on (t4.noc = t3.noc and t4.games = t3.games)
order by games,countrys)
select games,max(gold_medals)as max_gold,max(silver_medals) as max_gold,max(bronze_medals) as max_bronze from t5
group by games
order by games
before last query i got out like this
enter image description here
My output
enter image description here
Actual output needed
enter image description here
Im using Oracle database
my questing is - take a example in max_gold column have value of 25. that 25 gold value belong to germany. so i need output like germany-25 in max_gold column. that values group by games (ex - 1896 Summer,1900 Summer,1904 Summer). in second column You have 18.
There's no sample data so I made up my own.
I guess you don't need that many CTEs; one should do (temp in my example) as it fetches all medals "at once", while the rank analytic function ranks them (so rnk = 1 represents the top countries.
Why rank and not row_number? Because of ties - what if two (or more) countries have the same number of medals? That's also why final query utilizes listagg aggregate (and not e.g. min or max).
OK, here we go.
Sample data:
SQL> with
2 olympics_history (games, medal, region) as
3 (select 1896, 'Gold' , 'Austria' from dual union all
4 select 1896, 'Gold' , 'Austria' from dual union all
5 select 1896, 'Gold' , 'Belgium' from dual union all
6 select 1896, 'Silver', 'Germany' from dual union all
7 select 1896, 'Bronze', 'Austria' from dual union all
8 select 1896, 'Bronze', 'Austria' from dual union all
9 select 1896, 'Bronze', 'Canada' from dual union all
10 --
11 select 1900, 'Gold' , 'Germany' from dual union all
12 select 1900, 'Gold' , 'UK' from dual union all
13 select 1900, 'Silver', 'France' from dual union all
14 select 1900, 'Silver', 'France' from dual union all
15 select 1900, 'Silver', 'France' from dual union all
16 select 1900, 'Silver', 'Greece' from dual
17 ),
Query begins here:
18 temp as
19 (select games, medal, region, count(*) cnt,
20 rank() over (partition by games, medal order by count(*) desc) rnk
21 from olympics_history
22 group by games, medal, region
23 )
24 select games,
25 listagg(case when medal = 'Gold' then region || ' - ' || cnt end, ', ') within group (order by region) as gold,
26 listagg(case when medal = 'Silver' then region || ' - ' || cnt end, ', ') within group (order by region) as silver,
27 listagg(case when medal = 'Bronze' then region || ' - ' || cnt end, ', ') within group (order by region) as bronze
28 from temp
29 where rnk = 1
30 group by games;
GAMES GOLD SILVER BRONZE
---------- -------------------- -------------------- --------------------
1896 Austria - 2 Germany - 1 Austria - 2
1900 Germany - 1, UK - 1 France - 3
SQL>
I am looking for how to form a query, where I seek to find that the ordering accounts are interacting with the same beneficiary accounts 3 or more times. As I describe below.
Examples:
Account A sends account 1,2,and 3.
Account B sends account 1,2 and 3.
Account C sends account 1,2 and 3.
This is the table called TBL_ACCOUNTS
ordering account
beneficiary account
A
1
B
1
C
1
A
2
B
2
C
2
A
3
B
3
C
3
H
1
K
23
Z
329
W
3
I want to find all those accounts that meet this condition, that the ordering accounts are interacting with the same beneficiary accounts 3 or more times. The result you would expect to get is.
ordering account
beneficiary account
A
1
A
2
A
3
B
1
B
2
B
3
C
1
C
2
C
3
I hope you can guide me which way to go, because I'm a bit lost.
You can create a collection data type:
CREATE TYPE int_list IS TABLE OF INT;
and then you can use:
WITH accounts (ordering_account, beneficiary_account, accounts) AS (
SELECT t.*,
CAST(
COLLECT(beneficiary_account) OVER (PARTITION BY ordering_account)
AS int_list
)
FROM TBL_ACCOUNTS t
)
SELECT ordering_account,
beneficiary_account
FROM accounts a
WHERE EXISTS(
SELECT 1
FROM accounts x
WHERE a.ordering_account <> x.ordering_account
AND CARDINALITY(a.accounts MULTISET INTERSECT x.accounts) >= 3
-- Remove the next line if you want to return all accounts and not just the matched accounts
AND a.beneficiary_account = x.beneficiary_account
);
Which, for the sample data:
CREATE TABLE TBL_ACCOUNTS (ordering_account, beneficiary_account) AS
SELECT 'A', 1 FROM DUAL UNION ALL
SELECT 'B', 1 FROM DUAL UNION ALL
SELECT 'C', 1 FROM DUAL UNION ALL
SELECT 'A', 2 FROM DUAL UNION ALL
SELECT 'B', 2 FROM DUAL UNION ALL
SELECT 'C', 2 FROM DUAL UNION ALL
SELECT 'A', 3 FROM DUAL UNION ALL
SELECT 'B', 3 FROM DUAL UNION ALL
SELECT 'C', 3 FROM DUAL UNION ALL
SELECT 'C', 4 FROM DUAL UNION ALL
SELECT 'H', 1 FROM DUAL UNION ALL
SELECT 'K', 23 FROM DUAL UNION ALL
SELECT 'Z', 329 FROM DUAL UNION ALL
SELECT 'W', 3 FROM DUAL;
Outputs:
ORDERING_ACCOUNT
BENEFICIARY_ACCOUNT
A
1
A
3
A
2
B
1
B
3
B
2
C
1
C
2
C
3
If you want to do it without a collection then:
SELECT ordering_account,
beneficiary_account
FROM TBL_ACCOUNTS a
WHERE EXISTS(
SELECT 1
FROM TBL_ACCOUNTS x
WHERE a.ordering_account <> x.ordering_account
AND a.beneficiary_account = x.beneficiary_account
AND EXISTS(
SELECT 1
FROM TBL_ACCOUNTS l
INNER JOIN TBL_ACCOUNTS r
ON (l.beneficiary_account = r.beneficiary_account)
WHERE l.ordering_account = a.ordering_account
AND r.ordering_account = x.ordering_account
HAVING COUNT(*) >= 3
)
);
or:
SELECT ordering_account,
beneficiary_account
FROM TBL_ACCOUNTS a
WHERE EXISTS(
SELECT 1
FROM TBL_ACCOUNTS l
INNER JOIN TBL_ACCOUNTS r
ON ( l.beneficiary_account = r.beneficiary_account
AND l.ordering_account <> r.ordering_account )
WHERE l.ordering_account = a.ordering_account
GROUP BY r.ordering_account
HAVING COUNT(*) >= 3
AND COUNT(
CASE WHEN r.beneficiary_account = a.beneficiary_account THEN 1 END
) > 0
);
db<>fiddle here
Maybe something like this:
select ordering_account, beneficiary
from TBL_ACCOUNTS
group by ordering_account, beneficiary
having count(*) >= 3
order by ordering_account, beneficiary
SELECT T.ordering_account,T.beneficiary_account
FROM TBL_ACCOUNTS T
JOIN
(
SELECT Z.ordering_account
FROM TBL_ACCOUNTS Z
GROUP BY Z.ordering_account
HAVING COUNT(*)>2
)X ON T.ordering_account=X.ordering_account
ORDER BY T.ordering_account,T.beneficiary_account
or
SELECT X.ordering_account,X.beneficiary_account FROM
(
SELECT T.ordering_account,T.beneficiary_account,
COUNT(*)OVER(PARTITION BY T.ordering_account)XCOL
FROM TBL_ACCOUNTS T
)X WHERE X.XCOL=3
ORDER BY X.ordering_account,X.beneficiary_account
Self-join the table on the beneficiary account. Thus you get all ordering account pairs as often as they share the share3 beneficiary accounts. This means you can group by these pairs then and count.
The following query lists all entries of all ordering accounts for which exists another ordering account sharing at least three beneficiary accounts.
with share3 as
(
select a1.ordering_account as acc1, a2.ordering_account as acc2
from tbl_accounts a1
join tbl_accounts a2 on a2.beneficiary_account = a1.beneficiary_account
and a2.ordering_account > a1.ordering_account
group by a1.ordering_account, a2.ordering_account
having count(*) >= 3
)
select *
from tbl_accounts
where exists
(
select null
from share3
where share3.acc1 = tbl_accounts.ordering_account
or share3.acc2 = tbl_accounts.ordering_account
)
order by ordering_account, beneficiary_account;
I'm not sure I follow what you're asking, but it sounds like you simply need to include an ORDER BY clause.
At the end of your query just include
ORDER BY 'ordering account', 'beneficiary account'
The only thing that could change this is if you use different kinds of SQL that don't like single quotes. You may need to use [],"", or ``.
Sorry if I am not explaining this properly, I am relatively new to SQL.
In oracle have a table describing properties (city, property type, cost of rent per month, other information)
My question is: assuming 3 unique property types (hotel, house, empty lot), how can I show which cities do not have all 3 types of properties?
GROUP BY solution, make sure there are less than 3 different property types for each city returned:
select city
from tablename
where property_type in ('hotel', 'house', 'empty lot')
group by city
having count(distinct property_type) < 3
Your SQL query should be
SELECT City
FROM YourTable
WHERE hotel <> 'hotelname' and house <> 'housename' and emptylot <> 'name'
assuming
Hotel, House, Emptylot is column name in your database.
There are two ways,
GROUP BY
Analytic COUNT() OVER()
For example, Let's say I have sample data of 3 cities, where city 1 has all the property types satisfied, rest other cities are not having all the required property types.
Using GROUP BY
SQL> -- sample table data
SQL> WITH DATA AS(
2 SELECT 1 city, 'hotel' property FROM dual UNION ALL
3 SELECT 1 city, 'house' property FROM dual UNION ALL
4 SELECT 1 city, 'empty' property FROM dual UNION ALL
5 SELECT 2 city, 'hotel' property FROM dual UNION ALL
6 SELECT 2 city, 'house' property FROM dual UNION ALL
7 SELECT 2 city, 'scrap' property FROM dual UNION ALL
8 SELECT 3 city, 'empty' property FROM dual UNION ALL
9 select 3 city, 'house' property from dual
10 )
11 -- query
12 SELECT city
13 FROM data
14 WHERE property IN ('hotel', 'house', 'empty')
15 GROUP BY city
16 HAVING COUNT(property) < 3
17 /
CITY
----------
2
3
SQL>
Using Analytic COUNT() OVER()
SQL> -- sample table data
SQL> WITH DATA AS(
2 SELECT 1 city, 'hotel' property FROM dual UNION ALL
3 SELECT 1 city, 'house' property FROM dual UNION ALL
4 SELECT 1 city, 'empty' property FROM dual UNION ALL
5 SELECT 2 city, 'hotel' property FROM dual UNION ALL
6 SELECT 2 city, 'house' property FROM dual UNION ALL
7 SELECT 2 city, 'scrap' property FROM dual UNION ALL
8 SELECT 3 city, 'empty' property FROM dual UNION ALL
9 select 3 city, 'house' property from dual
10 )
11 -- query
12 SELECT DISTINCT city
13 FROM
14 (SELECT t.* ,
15 COUNT(property) OVER(PARTITION BY city ORDER BY city) rn
16 FROM DATA t
17 WHERE property IN ('hotel', 'house', 'empty')
18 )
19 WHERE rn < 3
20 /
CITY
----------
2
3
SQL>
Could be something like this:
SELECT City
FROM YourTable
WHERE [property type] != 'hotel' OR
[property type] != 'empty lot' OR
[property type] != 'house'
(Edited)Try this query :
select t.city from table_name t
where t.city NOT IN
(select city from table_name
where ( property_type ='hotel' or
property_type ='house' or
property_type ='Empty lot')
);
(Query returning cities where all three types of property are not present):
select t.city from table t inner join
(select city from table
where property_type not in ('House','Hotel','Empty lot')) x
on t.city=x.city
group by t.city
having count(*)<3 ;
I'm new to this sql world. After I'm a developer with limited knowledge in SQL & simple joins. I have an issue in writing sql join for one-to-many relationship. Here's my problem say -
For instance if I have a VendorsList table with:
id Name address
1 sales Japan
2 marketing US
And a VendorContacts table with:
id vendorid vendorempname
1 1 Tom
2 1 Bill
3 2 Jessy
4 1 Rachel
5 2 Rob
Now what I want after join is :
vendor_id Name address vendorempname
1 Sales Japan Tom
1 Sales Japan Bill
1 Sales Japan Rachel
2 Marketing US Jessy
2 Marketing US Rob
Can any one help in writing join for this please?
This should do the work
Select v.vendor_id, v.name, v.address, vc.vendorempname
from VendorContacts vc
join VendorList v
on v.id = vc.vendor_id
order by vc.vendor_id
SELECT b.vendor_id, a.Name, a.address, b.vendorempname
FROM VendorList a, VendorContact b
WHERE a.id = b.vendor_id
ORDER BY b.vendor_id;
Please have look this:
Select vl.vendorId,vl.name,vl.address,vc.vendorempname
from vendorlist as vl
right join VendorContacts as vc
on vl.vendorId = vc.vendorId
order by vc.vendorId asc
please try this and will get data that you want.
Try below query. It gives the exact results you are looking for except the order of the reps name..
WITH VENDORLIST AS
(SELECT '1' ID, 'sales' Name, 'Japan' address FROM DUAL
UNION
SELECT '2' ID, 'marketing' Name, 'US' address FROM DUAL),
VENDORCONTACTS AS
(
SELECT '1' ID, '1' vendorid, 'Tom' vendorempname FROM DUAL
UNION
SELECT '2' ID, '1' vendorid, 'Bill' vendorempname FROM DUAL
UNION
SELECT '3' ID, '2' vendorid, 'Jessy' vendorempname FROM DUAL
UNION
SELECT '4' ID, '1' vendorid, 'Rachel' vendorempname FROM DUAL
UNION
SELECT '5' ID, '2' vendorid, 'Rob' vendorempname FROM DUAL
)
SELECT VENDORID, NAME, ADDRESS, vendorempname FROM VENDORLIST, VENDORCONTACTS
WHERE VENDORCONTACTS.vendorid = VENDORLIST.ID
ORDER BY VENDORID, NAME, ADDRESS, vendorempname
;
Example.
I have two tables
CAR
id name
1 bmw
2 fiat
Car_info
car_id field_name country
1 year 2000
1 country germany
2 year 1988
2 country italy
How in one select query I can get this?
id name year country
1 bmw 2000 germany
2 fiat 1988 italy
Ideally, you should have a column in the car_info table specifying what type of information is being displayed on that line. i.e. Type 1 is the year, type 2 is the country.
You can get round this by joining on to the car_info table twice specifying an 'AND' condition on the join, like so:
SELECT car.ID, car.Name, ci1.country as [YEAR], ci2.country as COUNTRY
FROM car
INNER JOIN car_info AS ci1 ON Car.ID = ci1.car_id AND ci1.field_name = 'year'
INNER JOIN car_info AS ci2 ON Car.ID = ci2.car_id AND ci2.field_name = 'country'
Try this :
You can use PIVOT and then join with the Car table
with cte as
(
Select car_id,[year],[country]
from
(Select car_id,field_name,countryName
from car_info
)pv
pivot
(
max(countryName)
FOR field_name IN ([year],[country])
)pvt
)
Select c.name,ct.year,ct.country
from car c inner join cte ct
on ct.car_id=c.id
Demo in SQL Fiddle
WITH car AS (
SELECT 1 AS id, 'BMW' AS name FROM dual
UNION ALL
SELECT 2, 'Fiat' FROM dual
)
, car_info AS (
SELECT 1 AS car_id, 'Year' AS field_name, '2000' AS field_val FROM dual
UNION ALL
SELECT 1, 'Country', 'Germany' FROM dual
UNION ALL
SELECT 2, 'Year', '1988' FROM dual
UNION ALL
SELECT 2, 'Country', 'Italy' FROM dual
)
SELECT c.*, car_year.field_val AS yr, car_country.field_val AS country
FROM car c
JOIN car_info car_year ON c.id = car_year.car_id AND car_year.field_name = 'Year'
JOIN car_info car_country ON c.id = car_country.car_id AND car_country.field_name = 'Country'
;