get data from related tables in postgresql - sql

assuming these 3 tables
create table item(
item_id integer NOT NULL primary key,
name varchar(50) NOT NULL,
description varchar(150) NOT NULL,
in_stock integer NOT NULL
)
create table customer(
customer_id VARCHAR(9) NOT NULL primary key,
name VARCHAR(50) NOT NULL,
lastname VARCHAR(50) NOT NULL,
phone VARCHAR(15) NOT NULL,
join_date DATE NOT NULL
)
create table purchase(
purchase_id integer references item,
customer_id varchar(9) references customer,
purchase_date TIMESTAMP NOT NULL,
amount INTEGER NOT NULL,
PRIMARY KEY(purchase_id, customer_id, purchase_date)
)
how could I get each unique name and the total amount of items purchased?
how could I get each purchase name and the buyer's name and lastname?
how could I get each item and how many of it were sold?

The two topics you are looking to learn are how to use GROUP BY and how to JOIN tables. Here's an example (more or less) that answers your first question and uses both tools:
select
C.customer_id as customer_id,
max(C.name) as customer_name,
sum(amount) as total_amount
from customer C
left join purchase P on C.customer_id = P.customer_id
group by C.customer_id

Related

DateDiff asc and desc

SELECT DISTINCT
at.AccountId AS AccountId,
a.FirstName + ' ' + a.LastName AS [FullName],
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) AS LongestTrip,
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) AS ShortestTrip
FROM
Accounts a
JOIN
AccountsTrips at ON a.Id = AT.AccountId
JOIN
Trips t ON T.Id = AT.TripId
WHERE
a.MiddleName IS NULL AND t.CancelDate IS NULL
ORDER BY
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) DESC, ShortestTrip ASC
The code only orders the tables descending in LongestTrip and in ShortestTrip
SAMPLE DATA !
Find the longest and shortest trip for each account, in days. Filter the results to accounts with no middle name and trips, which are not cancelled (CancelDate is null).
Order the results by Longest Trip days (descending), then by Shortest Trip (ascending).
Examples
AccountId FullName LongestTrip ShortestTrip
------------------------------------------------------------
40 Winna Maisey 7 1
56 Tillie Windress 7 1
57 Eadith Gull 7 1
66 Sargent Rockhall 7 1
69 Jerome Flory 7 2
… … … …
The Tables are --
CREATE TABLE Cities
(
Id INT PRIMARY KEY IDENTITY,
Name NVARCHAR(20) NOT NULL,
CountryCode CHAR(2) NOT NULL
)
CREATE TABLE Hotels
(
Id INT PRIMARY KEY IDENTITY,
Name NVARCHAR(30) NOT NULL,
CityId INT FOREIGN KEY REFERENCES Cities(Id) NOT NULL,
EmployeeCount INT NOT NULL,
BaseRate DECIMAL(10,2)
)
CREATE TABLE Rooms
(
Id INT PRIMARY KEY IDENTITY,
Price DECIMAL(10,2) NOT NULL,
Type NVARCHAR(20) NOT NULL,
Beds INT NOT NULL,
HotelId INT FOREIGN KEY REFERENCES Hotels(Id) NOT NULL
)
CREATE TABLE Trips
(
Id INT PRIMARY KEY IDENTITY,
RoomId INT FOREIGN KEY REFERENCES Rooms(Id) NOT NULL,
BookDate DATE NOT NULL, CHECK(BookDate<ArrivalDate),
ArrivalDate DATE NOT NULL, CHECK(ArrivalDate<ReturnDate),
ReturnDate DATE NOT NULL,
CancelDate DATE
)
CREATE TABLE Accounts
(
Id INT PRIMARY KEY IDENTITY,
FirstName NVARCHAR(50) NOT NULL,
MiddleName NVARCHAR(20),
LastName NVARCHAR(50) NOT NULL,
CityId INT NOT NULL,
BirthDate DATE NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL
CONSTRAINT FK_CityId FOREIGN KEY (CityId)
REFERENCES Cities(Id)
)
CREATE TABLE AccountsTrips
(
AccountId INT FOREIGN KEY REFERENCES Accounts(Id) NOT NULL,
TripId INT FOREIGN KEY REFERENCES Trips(Id) NOT NULL,
Luggage INT NOT NULL, CHECK(Luggage >= 0)
)
You want to pick the longest and shortest trips per account from your data. As all you want to get from the trips is the duration, you can simply aggregate and show MIN and MAX duration:
SELECT
a.Id AS AccountId,
a.FirstName + ' ' + a.LastName AS [FullName],
MIN(DATEDIFF(day, T.ArrivalDate, T.ReturnDate)) AS LongestTrip,
MAXD(ATEDIFF(day, T.ArrivalDate, T.ReturnDate)) AS ShortestTrip
FROM
Accounts a
JOIN
AccountsTrips at ON a.Id = AT.AccountId
JOIN
Trips t ON T.Id = AT.TripId
WHERE
a.MiddleName IS NULL AND t.CancelDate IS NULL
GROUP BY
a.Id, a.FirstName, a.LastName
ORDER BY
LongestTrip DESC, ShortestTrip ASC;
If you wanted to show additional data from the trips, you would use window functions (probably MIN OVER and MAX OVER) and would then either show two rows per account or aggregate these two rows.

How to SELECT titles by sales, where sales less than N?

I have 2 tables: titles and sales created by queries:
CREATE TABLE [dbo].[sales]
(
[stor_id] [char] (4) NOT NULL,
[ord_num] [varchar] (20) NOT NULL,
[ord_date] [datetime] NOT NULL,
[qty] [smallint] NOT NULL, --quantity of sold books in this transaction
[payterms] varchar(12) NOT NULL,
[title_id] varchar(6) NOT NULL, --FOREIGN KEY REFERENCES titles(title_id),
CONSTRAINT FK_S_title_id
FOREIGN KEY (title_id) REFERENCES titles(title_id),
CONSTRAINT PK_sales
PRIMARY KEY CLUSTERED (stor_id, ord_num, title_id)
) ON [PRIMARY]
CREATE TABLE [dbo].[titles]
(
[title_id] varchar(6) CONSTRAINT PK_titles PRIMARY KEY CLUSTERED,
[title] varchar(80) NOT NULL, --name of book
[type] char(12) NOT NULL DEFAULT ('UNDECIDED'),
[pub_id] char(4) NULL,
[price] money NULL,
[advance] money NULL,
[royalty] int NULL,
[ytd_sales] int NULL,
[notes] varchar(200) NULL,
[pubdate] datetime NOT NULL DEFAULT (getdate())
) ON [PRIMARY]
I know that I can get a table of pairs title and sumOfSales by this query
SELECT title, SUM(qty) AS sumOfSales
FROM titles AS t, sales AS s
WHERE t.title_id = s.title_id
GROUP BY title
ORDER BY title
It results as this:
But I need to select all titles that are sold less or equal than 10 times and get something like this, without qty (nor sumOfSales) column:
title
=======================================
ABookThatIsSoldLessOrEqual10Times
NameOfAnotherBookSoldLessOrEqual10times
NameOfBookSoldLessOrEqual10times
EDIT: Thank you for answers. I wanted to use HAVING, but it doesn't do what I need to have in result of SELECT. I need to have a list of just titles of books that are sold only 10 or less times.
I could use a temporary table and do 2 SELECTs instead of one. But it is not very good practice.
AS you need to show a list of just titles of books then try this:
SELECT title FROM titles AS t inner join sale AS s
on t.title_id = s.title_id
GROUP BY title
HAVING sum(qty) <= 10
ORDER BY title
Use HAVING clause:
SELECT title, SUM(qty) AS sumOfSales
FROM titles AS t, sales AS s
WHERE t.title_id = s.title_id
GROUP BY title
HAVING SUM(qty) < 11
ORDER BY title
You can write the condition as SUM(qty) <= 10 if you like, but I find SUM(qty) < 11 more elegant.
You need to add HAVING to your query
SELECT title, sum(qty) AS sumOfSales
FROM
titles AS t
INNER JOIN sales AS s ON t.title_id = s.title_id
GROUP BY title
HAVING sum(qty) <= 10
ORDER BY title

SQL Relation, one-to-many with a current used row

in sql i have a table of items. An item can have multiples prices. But when i come to using the data, most of the time i only need the currentPrice that i can find with the date.
My question : Would it be wrong to have 2 relation on price where and Item would have a relation with the currentPrice and all the price. Do there is a solution for this kind of problem to prevent complication in my mvc. Im currently using a viewModel with and the current price but if i could handle this in the database it seem better to me. (tell me if its not).
Note: I don't think this exact methode would work since I would need to create my price table before my item table and the same for the item table. But this show my problem.
Thanks alot for your help.
CREATE TABLE Item.Items
(
ItemId INT NOT NULL
CONSTRAINT PK_Items_ItemId
PRIMARY KEY
IDENTITY,
--------------------------------------------------
--CurrentPriceId INT NOT NULL
CONSTRAINT FK_Prices_Items
FOREIGN KEY REFERENCES Item.Items (ItemId),
--------------------------------------------------
Name VARCHAR(100) NOT NULL,
Points INT NOT NULL DEFAULT 0,
Description VARCHAR(5000) NULL
)
CREATE TABLE Item.Prices
(
PriceId INT NOT NULL
CONSTRAINT PK_ItemPrices_ItemPriceId
PRIMARY KEY
IDENTITY,
ItemId INT NOT NULL
CONSTRAINT FK_ItemPrices_Item
FOREIGN KEY REFERENCES Item.Items (ItemId),
EffectiveDate DATETIME NOT NULL,
Value MONEY NOT NULL
)
better would be:
CREATE TABLE Item.Items
(
ItemId INT NOT NULL
CONSTRAINT PK_Items_ItemId
PRIMARY KEY IDENTITY,
--------------------------------------------------
CurrentPrice MONEY NOT NULL, -- optional optimization, redundant as
-- current price is available from item.Prices
--------------------------------------------------
Name VARCHAR(100) NOT NULL,
Points INT NOT NULL DEFAULT 0,
Description VARCHAR(5000) NULL
)
CREATE TABLE Item.Prices
(
ItemId INT NOT NULL
CONSTRAINT FK_ItemPrices_Item
FOREIGN KEY REFERENCES Item.Items (ItemId),
EffectiveUtc DATETIME NOT NULL,
Price MONEY NOT NULL,
Constraint [ItemPricesPK] PRIMARY KEY CLUSTERED
(
[ItemId] ASC,
[EffectiveUtc] ASC
)
)
As mentioned by #popovitz, you can always get the price as of any specific date using a correlated subquery ...
Select i.name, p.price
From Item.Items i
Join Item.Prices p
ON i.ItemId = p.ItemId
Where p.EffectiveUtc =
(Select Max(EffectiveUtc )
From Item.Prices
Where ItemId = i.ItemId
And EffectiveUtc <= #asOfDate)
I would not recommend adding a currentPrice column, because you would have to constantly make sure that the item table is being updated whenever there a new price becomes effective. It's not that hard to query the current price when you have a table that contains the prices with an EffectiveDate column:
SELECT i.name, p.price FROM Items i
INNER JOIN Prices p ON i.ItemId = p.ItemId
WHERE p.EffectiveDate =
(SELECT MAX(EffectiveDate) FROM Price
WHERE EffectiveDate <= SYSDATE
AND ItemId = i.ItemId)
This will select the correct price for the current system time, assuming that (EffectiveDate, ItemId) is unique for the table prices.

how to create a query two or more values?

I would need if I could help with the following code:
CREATE TABLE `car` (
`car_id` int(10) unsigned NOT NULL auto_increment,
`car_name` varchar(25) NOT NULL,
`car_year` date NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB;
CREATE TABLE `people` (
`peo_id` int(10) unsigned NOT NULL auto_increment,
`peo_name` varchar(50) NOT NULL,
`peo_surname` varchar(200) NOT NULL,
`car_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`fav_id`),
KEY `FK_Favorites` (`user_id`),
CONSTRAINT `FK_Favorites` FOREIGN KEY (`car_id`) REFERENCES `car` (`car_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
I have questions, show all cars that are more than 3 people ordered by year of car.
Thank you very much, sorry for my bad English
SELECT
c.car_id
,c.car_year
,COUNT(p.peo_id)
FROM car c
INNER JOIN people p on c.card_id = p.car_id
GROUP BY c.car_id ,c.car_year
HAVING COUNT(p.peo_id) > 3
ORDER BY c.car_year
First, find number of cars with more than three people
select car_id
from people
group by car_id
having count(*) > 3
Now, get the car details
select * from car
where car_id in
(
select car_id
from people
group by car_id
having count(*) > 3
)
order by car_year

Returning multiple aggregate functions as rows

I need some help formulating a select statement. I need to select the total quantity shipped for each part with a distinct color. So the result should be a row with the color name and the total.
Here's my schema:
create table s
( sno char(5) not null,
sname char(20) not null,
status smallint,
city char(15),
primary key (sno)
);
create table p
( pno char(6) not null,
pname char(20) not null,
color char(6),
weight smallint,
city char(15),
primary key (pno)
);
create table sp
( sno char(5) not null,
pno char(6) not null,
qty integer not null,
primary key (sno, pno)
);
As your schema stands each product PNO has only one colour, so I'm not sure what your question actually requires.
Sales by product:
select p.pno
, sum (sp.qty)
from p join sp on (p.pno = sp.pno)
group by p.pno;
Sales by colour:
select p.color
, sum (sp.qty)
from p join sp on (p.pno = sp.pno)
group by p.color;
edit
To get sales for a specific colour is easy:
select sum (sp.qty)
from p join sp on (p.pno = sp.pno)
where p.color = 'BLUE';