oracle SQL - unpivot - sql

Can u tell me, why it wont work?:)
select *
from( select r.region_id, c.country_id
from countries c join regions r on r.region_id = c.region_id)
unpivot(
valuee for columnValue in (r.region_id))
ORA-01748: only simple column names allowed here
01748. 00000 - "only simple column names allowed here"

With this part:
select *
You are selecting columns: region_id and country_id from your inner select. So you do not need r.region_id in your UNPIVOT section, only region_id.
This code is correct(without error):
select *
from(select r.region_id
, c.country_id
from countries c
join regions r on r.region_id = c.region_id)
unpivot(valuee for columnValue in (region_id));

Your query is strange and it isn't really clear what you're trying to achieve; but following up on #VBoka's correction of your code, you don't actually need the join at all (unless you have countries with non-existent regions) - you can do:
select *
from (
select region_id, country_id
from countries
)
unpivot(valuee for columnValue in (region_id));
But you can get the same result without unpivoting; with the join if you have a real reason to include it:
select c.country_id, 'REGION_ID' as columnvalue, r.region_id as valuee
from countries c
join regions r on r.region_id = c.region_id;
or without the join:
select country_id, 'REGION_ID' as columnvalue, region_id as valuee
from countries;
Either way you get a result set with one row for every country.

Related

Returning the entity with max number of participants

I'm using pgsql to find the cage number (cno) that holds the largest number of animals but doesn't have a bird in it.
The way I tried to do it is by creating a table that counts the number of animals in each cage (not including those with birds) and then return the ones where the count equals the max value.
select temp.cno,temp.size
from
(select cage.cno,cage.size,count(*) as q
from cage,animal
where cage.cno = animal.cno and cage.cno not in (select cno from animal where lower(atype)='sheep')
group by cage.cno,cage.size) as temp
where temp.q = (select max(q) from temp)
I'm getting the following error message
ERROR: relation "temp" does not exist
LINE 7: where temp.q = (select max(q) from temp)
Any idea how to overcome this issue? Why isn't temp recognized within the last sub query?
Here are the tables
cage (cno, type, size)
animal (aid, aname, cno, atype)
You already found out that a subquery defined in the FROM is not visible inside another subquery defined in the WHERE clause.
This is easily solvable with the use of a CTE (with a proper join):
WITH temp AS (
SELECT c.cno, c.size, COUNT(*) AS q
FROM cage c INNER JOIN animal a
ON a.cno = c.cno
WHERE c.cno NOT IN (SELECT cno FROM animal WHERE LOWER(atype) = 'bird')
GROUP BY c.cno, c.size
)
SELECT cno, size
FROM temp
WHERE q = (SELECT MAX(q) FROM temp);
But, if there is a case that in a cage exist animals of more than one type then the condition:
c.cno NOT IN (SELECT cno FROM animal WHERE LOWER(atype) = 'bird')
is not correct, because it returns all cages which contain other types than birds without a restriction that there are only other types than birds.
You can apply this restriction with aggregation.
If you want/expect only 1 cage as result:
SELECT c.cno, c.size
FROM cage c INNER JOIN animal a
ON a.cno = c.cno
GROUP BY c.cno
HAVING MAX((LOWER(a.atype) = 'bird')::int) = 0
ORDER BY COUNT(*) DESC LIMIT 1;
If you want more than one cages with the largest number of animals, use RANK() window function:
WITH cte AS (
SELECT c.cno, c.size,
RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM cage c INNER JOIN animal a
ON a.cno = c.cno
GROUP BY c.cno
HAVING MAX((LOWER(a.atype) = 'bird')::int) = 0
)
SELECT cno, size FROM cte WHERE rnk = 1;
Note that since cno is the PRIMARY KEY of cage you only need to group by cno.
I solved it by ordering the results descending and using limit 1 to show the first row (which is the max)

SQL query using SET operator

Using a SET command, show all the countries in region 1 that do not have a location.
Display only the country_id.
This is my query and it's wrong. I can't figure it out.
SELECT c.country_id
FROM Countries c
WHERE r.region_id = 1
MINUS
SELECT c2.country_id
FROM Countries c2
WHERE l.location_id IS NULL;
You can use country and location table as follows:
SELECT c.country_id
FROM Countries c
WHERE c.region_id = 1
MINUS
SELECT c2.country_id
FROM location c2;

Which Join for SQL plus query

I have 4 tables, I would like to select one column from each table, but only if the department has both 'Mick' and 'Dave working in it (must have both names, not one or the other). But it does not seem to be working properly:
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM STUDENTS
NATURAL JOIN SCHOOLS NATURAL JOIN TOWNS NATURAL JOIN
COUNTIES
WHERE FIRST_NAME IN ('Mick','Dave)
/
I'm going wrong somewhere (probably lots of places :( ). Any help would be great
Don't use NATURAL JOIN. It is an abomination, because it does not take properly declared foreign key relationships into account. It only looks at the names of columns. This can introduce really hard to find errors.
Second, what you want is aggregation:
select sc.SCHOOL_NAME, t.TOWN, c.COUNTY
from STUDENTS st join
SCHOOLS sc
on st.? = sc.? join
TOWNS t
on t.? = ? join
COUNTIES c
on c.? = t.?
where FIRST_NAME in ('Mick', 'Dave')
group by sc.SCHOOL_NAME, t.TOWN, c.COUNTY
having count(distinct st.first_name) = 2;
The ? are placeholders for table and column names. If you are learning SQL, it is all the more important that you understand how columns line up for joins in different tables.
A where clause can only check the values in a single row. There is a separate row for each student, so there is no way -- with just a where -- to find both students. That is where the aggregation comes in.
You need at least three Join conditions, and properly end the string Dave with quote :
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM SCHOOLS h
JOIN TOWNS t ON (t.id=h.town_id)
JOIN COUNTIES c ON (t.county_id=c.id)
WHERE EXISTS ( SELECT school_id
FROM STUDENTS s
WHERE s.first_name in ('Mick','Dave')
AND school_id = h.id
GROUP BY school_id
HAVING count(1)>1
);
SQL Fiddle Demo
You can use an analytic function in a sub-query to count the students who have the name Mick or Dave for each school_id (assuming that is your identifier for a school):
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM ( SELECT *
FROM (
SELECT d.*,
COUNT(
DISTINCT
CASE WHEN FIRST_NAME IN ( 'Mick', 'Dave' ) THEN FIRST_NAME END
) OVER( PARTITION BY school_id )
AS num_matched
FROM STUDENTS d
)
WHERE num_matched = 2
)
NATURAL JOIN SCHOOLS
NATURAL JOIN TOWNS
NATURAL JOIN COUNTIES;
SQLFiddle
You would also be better to use an INNER JOIN and explicitly specify the join condition rather than relying on NATURAL JOIN.

SQL Logic or Query to find what is missing

Hi all I need your help on the following logic. Currently I have a table that has 300 records, that are related but on this new tables I have the columns called them country, POS so for each combination of country + POs I should have 1 record of table A.
but the situation is that when I am checking the last table someone only inserted some records of table A into table b, and now I have to find what are the missing combination.
could you guide me on the logic that I should use for this, any question please let me know.
Example
Table A name Categories
Milk
Hot Sauces
Meat
Table B
Category POS Country
Milk DF Mexico
Meat DF Mexico
Hot Sauces DF Mexico
Milk CC Canada
Like you can see Canada still missing 2 categories but this table have all Americas countries so let say I have 20 countries. So 20 multiple by 300 categories I should have 6000 distinct records or more because each country have different quantities of POS, right, but someone only inserted let say 3600 records so now I have to find what combination are missed.
If you Don't have a country table you can derive one by selecting DISTINCT Country from your TableB. Then cross join that with Categories for a Cartesian Join (all possible combinations) between Countries and Categories.
SELECT countries.country, c.Category
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
WHERE
b.Cateogry IS NULL
If you actually need All Possible Combinations of POS and Country and Categories. In this case it sounds like POS is more like a store than a point of sale but same concept. Just derive a POS table if you don't have one and cross join it with the cross join of countries and categories.
SELECT
countries.country, c.Category, pos.POS
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
CROSS JOIN (SELECT DISTINCT POS
FROM
#TableB) as pos
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
AND pos.POS = b.POS
WHERE
b.Cateogry IS NULL
But I would guess that not every store is in every country so you probably want to constrain the POS combiantions to POS's that are available in a particular country. Again you can derive the table if you don't have one this time include Country and do an inner join between the derived country table and it.
SELECT
countries.country, c.Category, pos.POS
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
INNER JOIN (SELECT DISTINCT Country, POS
FROM
#TableB) as pos
ON countries.Country = pos.Country
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
AND pos.POS = b.POS
WHERE
b.Cateogry IS NULL
test data used:
DECLARE #Categories AS TABLE (Category VARCHAR(25))
DECLARE #TableB AS TABLE (Cateogry VARCHAR(25),POS CHAR(2), Country VARCHAR(25))
INSERT INTO #Categories VALUES ('Milk'),('Hot Sauces'),('Meat')
INSERT INTO #TableB VALUES ('Milk','DF','Mexico'),('Meat','DF','Mexico'),('Hot Sauces','DF','Mexico'),('Milk','CC','Canada'),('Milk','XX','Canada')
Hi, You can use below logic to get missing data,
SELECT column_name FROM tableA WHERE column_name NOT IN
(SELECT column_name FROM tableB)
Change the required column names and table names in the query. Use same column names in all three places

How to select members of a special kind of relationship in sql

I have 3 table as follow :
s(s# int,sname nchar(10))
p(p# int,pname nchar(10))
sp(s# int,p# int)
table "s" is table of suppliers and "s#" is primary key of it.also table "p" is table of products and "p#" is primary key on it."s#" and "p#" are foreign key in table "sp".
now my question is "How can I select name of suppliers from table "s" which producing all of products in table "p"...
SELECT p.*, s.sname FROM s, sp, p WHERE s.s# = sp.s# AND sp.p# = p.p#;
This statement will output all products with all their suppliers.
Now we group my suppliers, and count how many products they provide:
SELECT s.sname, count(*) FROM s, sp, p WHERE s.s# = sp.s# AND sp.p# = p.p# GROUP BY s.s#;
Now we know exacly, how many products each supplier provides. And we also know, how many products are in the productstable:
SELECT count(*) FROM p;
If you compare these values, you get your desired result:
SELECT amounts.name FROM
( SELECT s.sname AS name, count(*) AS offers
FROM s, sp, p
WHERE s.s# = sp.s# AND sp.p# = p.p#
GROUP BY s.s# ) amounts, -- this is a temp. tablename
( SELECT count(*) AS avaiable FROM p ) countTbl
WHERE amounts.offers = countTbl.avaiable;
Notice, that I didn't test the query. But you should get an idea on how to solve this problem.
It might also be possible to write this query more efficient, but this one can be understood easily.
There are two ways to do this, the first I thought of was to invert the logic.
Rather than attempting to find every entry of P let's just look for any that don't exist, then exclude those entries from S:
SELECT *
FROM S
WHERE
NOT EXISTS (
SELECT *
FROM P
LEFT JOIN SP
ON P.P# = SP.P#
AND SP.S# = S.S#
WHERE
SP.P# IS NULL
)