How to join tables , query sql - sql

I have the following recipe database tables and their data .
how i can find the total number of recipes , number of category for each Ingredient ? I used many joining methods but i couldn't do the query i want.
I need as out put
Ingredient id , how much recipe we can find this ingredient in, how much categories we can find this ingredients in.
This is my attempt
The problem with my attempt is if i had ingredient who is in one recipe and in two categories
It will show in results that this ingredient is in 2 recipe , 2 categories
SELECT
I.idIng,COUNT(CI.idcat)AS "CAT FOR ING" , COUNT(RI.idRecipe )AS "RECETTE
FOR
ING"
FROM
INGREDIENT I
LEFT JOIN
Ingredient_Recipe RI ON I.idIng = ri.idIng
RIGHT JOIN
Ingredient_Catigory CI ON I.idIng = CI.idIng
GROUP BY
I.idIng
ORDER BY
I.idIng;
Below is some test data:
Here i created my category its will have many to many relation with Ingredient.
-- creating table cat category
CREATE TABLE category (
idCat INT NOT NULL PRIMARY KEY,
nomCat INT NOT NULL
);
Here i created my Recipe table its will have many to many relation with Ingredient.
-- creating table cat Recipe
CREATE TABLE Recipe(
idRecipe INT NOT NULL PRIMARY KEY,
nameRecipe VARCHAR2(30) NOT NULL
);
This is my Ingredient table that will be link with both Recipe ,category .
-- creating table Ingredient
CREATE TABLE Ingredient(
idIng INT NOT NULL PRIMARY KEY ,
nameIng VARCHAR2(30) NOT NULL
);
This is the intermediate table between Ingredient ,category because the relation is many to many.
-- creating table Ingredient_category
CREATE TABLE Ingredient_category (
idIng INT NOT NULL,
idCat INT NOT NULL,
CONSTRAINT idIng_FK FOREIGN KEY (idIng) REFERENCES Ingredient(idIng),
CONSTRAINT idCat_FK FOREIGN KEY (idCat) REFERENCES category(idCat)
);
This is the intermediate table between Ingredient ,Recipe because the relation is many to many.
-- creating table Ingredient_Recipe
CREATE TABLE Ingredient_Recipe(
idIng INT NOT NULL,
idRecipe INT NOT NULL,
CONSTRAINT idIngRecipe_FK FOREIGN KEY (idIng) REFERENCES
Ingredient(idIng),
CONSTRAINT idRecipe_FK FOREIGN KEY (idRecipe) REFERENCES Recipe(idRecipe)
);
Here we insert the data for testing.
-- insert data into Recipe
INSERT INTO Recipe VALUES(1,'SOUP');
INSERT INTO Recipe VALUES(2,'FRIED');
INSERT INTO Recipe VALUES(3,'BURGER');
-- insert data into category
INSERT INTO category VALUES(1,'VEGES');
INSERT INTO category VALUES(2,'DAIRY');
INSERT INTO category VALUES(3,'MEAT');
INSERT INTO category VALUES(4,'ANIMAL PRODUCT');
-- insert data into Ingredient
INSERT INTO Ingredient VALUES (1,'Eggs');
INSERT INTO Ingredient VALUES (2,'milk');
INSERT INTO Ingredient VALUES (3,'Beef');
INSERT INTO Ingredient VALUES (4,'chess');
-- insert data into Ingredient_Catigory
INSERT INTO Ingredient_Catigory VALUES(1,4);
INSERT INTO Ingredient_Catigory VALUES(2,2);
INSERT INTO Ingredient_Catigory VALUES(2,4);
INSERT INTO Ingredient_Catigory VALUES(3,3);
INSERT INTO Ingredient_Catigory VALUES(3,4);
INSERT INTO Ingredient_Catigory VALUES(4,2);
INSERT INTO Ingredient_Catigory VALUES(4,4);
-- insert data into Ingredient_Recip
INSERT INTO Ingredient_Recipe VALUES (1,2);
INSERT INTO Ingredient_Recipe VALUES (1,3);
INSERT INTO Ingredient_Recipe VALUES (2,1);
INSERT INTO Ingredient_Recipe VALUES (3,3);
INSERT INTO Ingredient_Recipe VALUES (3,2);

I think you're looking for COUNT(DISTINCT value), you're almost there. Try something like this;
SELECT
I.idIng,
COUNT(DISTINCT CI.idCat)AS [Categories],
COUNT(DISTINCT RI.idRecipe)AS [Recipes]
FROM #INGREDIENT I
LEFT JOIN #Ingredient_Recipe RI ON I.idIng = ri.idIng
LEFT JOIN #Ingredient_Category CI ON I.idIng = CI.idIng
GROUP BY
I.idIng
ORDER BY
I.idIng
The results look like this;
idIng Categories Recipes
1 1 2
2 2 1
3 2 2
4 2 0
Please note, I think a bit of the spelling was incorrect in the sample data but I've corrected it on my test system (and I've used #TempTables). I've changed your RIGHT JOIN to a LEFT JOIN (as a note, I've never seen a need to use RIGHT JOIN in production code, try to avoid them).
Edit: I've just noticed that this is now an Oracle question, the query above has only been tested on SQL Server although a cursory glance at the documentation shows that the syntax should be the same for Oracle too.

select * from Ingredient_Recipe a join Ingredient_category b on a.idIng=b.idIng join Ingredient c on a.idIng=c.idIng join Recipe d on a.idRecipe=d.idRecipe join category e on b.idCat=e.idCat
try this out

Related

Get column values from mapping tables "id | value" binding

I am trying to get all the columns associated to with my item, some columns are "key | value" paired and that's where my problem is. My idea for a structure looks like this
I can retrieve 1 item from Posts along with all associated tag names with this query, but the problem is that I just can get 1 post
SELECT TOP(10)
bm.title, bm.post_id,
a.name AS tag1, b.name AS tag2, c.name AS tag3, d.name AS tag4
FROM
Posts AS bm
INNER JOIN
Tagmap AS tm
INNER JOIN
Tag AS a ON a.tag_id = tm.tag_id1
INNER JOIN
Tag AS b ON b.tag_id = tm.tag_id2
INNER JOIN
Tag AS c ON c.tag_id = tm.tag_id3
INNER JOIN
Tag AS d ON d.tag_id = tm.tag_id4
ON bm.post_id = tm.post_id
Here is the DDL for the table, or you can get it from this PasteBin link:
CREATE TABLE Tag
(
tag_id int NOT NULL identity(0,1) primary key,
name nvarchar(30) NOT NULL,
);
CREATE TABLE Tagmap
(
id int NOT NULL identity(0,1) primary key,
post_id int FOREIGN KEY REFERENCES Posts(post_id),
tag_id1 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id2 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id3 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id4 int FOREIGN KEY REFERENCES Tag(tag_id)
);
CREATE TABLE Posts
(
post_id int NOT NULL identity(0,1) primary key,
title nvarchar(50) not null,
);
INSERT INTO Posts VALUES ('Title1');
INSERT INTO Posts VALUES ('Title2');
INSERT INTO Tag VALUES ('Tag number one');
INSERT INTO Tag VALUES ('Tag number two');
INSERT INTO Tag VALUES ('Tag number three');
INSERT INTO Tag VALUES ('Tag number four');
INSERT INTO Tagmap VALUES (0, 0, 1, 2, 3);
My question: is my approach totally off? Should I change the structure or is it good?
If so how can it be better and how can I retrieve all these "key | value" columns along with my posts?
First, you should fix your data structure, so you have one row in tagMap per post_id and tag_id -- not four!
But event with your current structure, I imagine that not all posts have four tags. So, with your current data model you should be using LEFT JOIN, rather than INNER JOIN.

How would I select from two different tables and get the following in Oracle

I need to do the following:
From each branch, find the manager names and the #customers they are managing and the total deposit balance of the customers they manage
My database looks like this
drop table branch;
CREATE TABLE branch (
BNO NUMBER(1,0),
MANAGER_NAME VARCHAR(6),
Salary NUMBER(6,0),
MGRSTARTDATE TIMESTAMP (2)
);
INSERT INTO branch VALUES
(1,'BOB',100000,'19-JUN-2001');
INSERT INTO branch VALUES
(2,'CHRIS',150000,'01-Jan-2005');
INSERT INTO branch VALUES
(3,'ANGELA',90000,'22-May-1998');
INSERT INTO branch VALUES
(4,'KIM',90000,'29-May-1996');
drop table account;
CREATE TABLE account (
ACC NUMBER(3,0),
CNAME VARCHAR(4),
BNO NUMBER(1,0),
BALANCE NUMBER(4,0)
);
INSERT INTO account VALUES
(101,'LISA',1,100);
INSERT INTO account VALUES
(102,'LISA',2,500);
INSERT INTO account VALUES
(103,'TOM',1,400);
INSERT INTO account VALUES
(104,'JOHN',3,1200);
INSERT INTO account VALUES
(105,'TOM',3,900);
All I have so far and don't know what to do next is
SELECT MANAGER_NAME
FROM branch;
I think I need to do some type of join but don't know how.
Try this
select br.manager_name, acc.cname from account acc
left join branch br
ON acc.bno = br.bno
And the below will sum balance that manager manage
select br.manager_name, acc.cname, sum(acc.balance) from account acc
left join branch br
ON acc.bno = br.bno
group by br.bno, br.manager_name
You'll want to use a OUTER JOIN (this is the same as a LEFT/RIGHT join). An outer join takes all values from the one table and adds data from the second table matching on a key. The key you'd use to match is the "BNO" value -- hoangnh's example covers this
For reference, the other type of join is an INNER JOIN which wouldn't work since that would only return values with key matches in both tables (in your example, doing an inner join would exclude BNO=4 from the final result. Doing an outer join would have BNO=4 included with nulls for the customer values)

Select rows that have a specific set of items associated with them through a junction table

Suppose we have the following schema:
CREATE TABLE customers(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE items(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE customers_items(
customerid INTEGER,
itemid INTEGER,
FOREIGN KEY(customerid) REFERENCES customers(id),
FOREIGN KEY(itemid) REFERENCES items(id)
);
Now we insert some example data:
INSERT INTO customers(name) VALUES ('John');
INSERT INTO customers(name) VALUES ('Jane');
INSERT INTO items(name) VALUES ('duck');
INSERT INTO items(name) VALUES ('cake');
Let's assume that John and Jane have id's of 1 and 2 and duck and cake also have id's of 1 and 2.
Let's give a duck to John and both a duck and a cake to Jane.
INSERT INTO customers_items(customerid, itemid) VALUES (1, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 2);
Now, what I want to do is to run two types of queries:
Select names of customers who have BOTH a duck and a cake (should return 'Jane' only).
Select names of customers that have a duck and DON'T have a cake (should return 'John' only).
For the two type of queries listed, you could use the EXISTS clause. Below is an example query using the exists clause:
SELECT cust.name
from customers AS cust
WHERE EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'duck')
AND NOT EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'cake')
Here is a working example: http://sqlfiddle.com/#!6/3d362/2

SQL : Join of three tables

I am new to SQL and am learning joins of tables now.
I am stuck at joining three tables.
(I have given the rows I have inserted to the tables also for your reference.)
My tables are
--Table1
create table sql_students(
stu_studentid int not null primary key,
stu_name varchar(100) not null,
stu_regnnumber bigint unique not null
)
--Rows inserted to Table1
insert into sql_students (stu_studentid,stu_name,stu_regnnumber) values (1,'John',194300)
insert into sql_students (stu_studentid,stu_name,stu_regnnumber) values (2,'Joy',959595)
insert into sql_students (stu_studentid,stu_name,stu_regnnumber) values (3,'Lucy',474848)
--Table2
create table sql_exam(
exa_examid bigint not null primary key,
exa_name varchar(100) not null,
exa_maxmark decimal(5,2) not null,
exa_minmarkreqdforpass decimal(5,2) not null,
exa_examscheduletime datetime not null
)
--Rows inserted into Table2
insert into sql_exam(exa_examid,exa_name,exa_maxmark,exa_minmarkreqdforpass,exa_examscheduletime) values (1,'Maths',100,40,'2012-10-10 10:00')
insert into sql_exam(exa_examid,exa_name,exa_maxmark,exa_minmarkreqdforpass,exa_examscheduletime) values (2,'English',75,35,'2012-10-11 10:00')
--Table3
create table sql_studentmarks(
stm_studentid int foreign key references sql_students(stu_studentid),
stm_examid bigint foreign key references sql_exam(exa_examid),
stm_mark decimal(5,2)
)
--Rows inserted into Table3
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (1,1,80)
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (2,1,90)
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (3,1,40)
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (1,2,70)
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (2,2,60)
insert into sql_studentmarks(stm_studentid,stm_examid,stm_mark) values (3,2,17)
I need guidelines to get
All students who passed all exams
All students who attended all exams
All students and their mark difference in Maths and English.
Thanks in advance.
For #1
SELECT s.stu_name
FROM sql_studentmarks AS m
JOIN sql_students AS s ON m.stm_studentid = s.student_id
JOIN sql_exam AS x ON COUNT(m.stm_examid) = COUNT (x.stm_examid)
WHERE m.stm_mark >= x.exa_minmarkreqdforpass GROUP BY s.stu_studentid;
For #2 you can base on the exam count, perhaps creating a var in the select statement:
SELECT s.stu_name
FROM sql_studentmarks AS m
JOIN sql_students AS s ON m.stm_studentid = s.stu_studentid
RIGHT OUTER JOIN sql_exam AS x ON COUNT(m.stm_examid) = COUNT (x.stm_examid)
AND m.stm_examid IS NOT NULL
GROUP BY s.stu_student_id;
FOR #3 use the examples from above, simple joins will do:
SELECT s.stu_name, x.exa_name, m.stm_mark
FROM sql_studentmarks AS m
JOIN sql_students AS s ON m.stm_studentid = s.stu_studentid
JOIN sql_exam x ON m.stm_examid = x.exa_examid AND s.stu_studentid = x.exa_studentid;
The code below shows you the first two queries. Your joins are OK.. Try these in SQL Fiddle and then see if you can work the third query out
Query #1: (You were very close, just needed the distinct keyword)
select stm_studentid,stu_name
from sql_exam
join sql_studentmarks on exa_examid=stm_examid
and stm_mark>exa_minmarkreqdforpass
inner join sql_students on stu_studentid=stm_studentid
group by stm_studentid,stu_name
having count(*) = (select count(*) from sql_exam)
Query #2: -
The group by lets you count the number of exams taken.
The having lets you compare the count to number of exams available
select stu_name,count(*) as NumExamsTaken
from sql_studentmarks
join sql_students on stu_studentid=stm_studentid
group by stu_name
having count(*) = (select count(*) from sql_exam)
Query #3:
This should get you started
select stu_studentid,stu_name,
MG.stm_mark as MathGrade,
EG.Stm_mark as EnglishGrade
from sql_students
join sql_exam MATH on MATH.exa_name='MATHS'
join sql_exam ENG on ENG.exa_name='ENGLISH'
join sql_studentmarks MG on MG.stm_studentid=stu_studentid
and MG.stm_examid=MATH.exa_examId
join sql_studentmarks EG on EG.stm_studentid=stu_studentid
and EG.stm_examid=ENG.exa_examId

SQL Simple SELECT Query

create table Person(
SSN INT,
Name VARCHAR(20),
primary key(SSN)
);
create table Car(
PlateNr INT,
Model VARCHAR(20),
primary key(PlateNr)
);
create table CarOwner(
SSN INT,
PlateNr INT,
primary key(SSN, PlateNR)
foreign key(SSN) references Person (SSN),
foreign key(PlateNr) references Car (PlateNr)
);
Insert into Person(SSN, Name) VALUES ('123456789','Max');
Insert into Person(SSN, Name) VALUES ('123456787','John');
Insert into Person(SSN, Name) VALUES ('123456788','Tom');
Insert into Car(PlateNr, Model) VALUES ('123ABC','Volvo');
Insert into Car(PlateNr, Model) VALUES ('321CBA','Toyota');
Insert into Car(PlateNr, Model) VALUES ('333AAA','Honda');
Insert into CarOwner(SSN, PlateNr) VALUES ('123456789','123ABC');
Insert into CarOwner(SSN, PlateNr) VALUES ('123456787','333AAA');
The problem I'm having is the SELECTE query I wanna make. I wan't to be able to SELECT everything from the Person and wan't the include the PlateNr of the car he's the owner of, an example:
PERSON
---------------------------------
SSN NAME Car
123456789 Max 123ABC
123456787 John 3338AAA
123456788 Tom
----------------------------------
So, I want to be able to show everything from the Person table and display the content of CarOwner aswell if the person is in fact a CarOwner. What I have so far is: "SELECT * from Person, CarOwner WHERE Person.SSN = CarOwner.SSN;". But this obviously results in only showing the person(s) that are CarOwners.
Hope I explained me well enough, Thanks.
Try this:
SELECT p.*, c.*
FROM Person p
LEFT OUTER JOIN CarOwner co
ON p.SSN = co.SSN
LEFT OUTER JOIN Car c
ON co.PlateNr = c.PlateNr
Show SQLFiddle
P.S. I've changed the type of your primary key PlateNr (in varchar and not in int)
select ssn, name, car
from Person p
LEFT OUTER JOIN CarOwner co
ON p.SSN = co.SSN
LEFT OUTER JOIN Car c
ON co.PlateNr = c.PlateNr