SQL query with one-to-many relationship with PostgreSQL - sql

I'm trying to aggregate a list of cart items with a query that relates through a line_items table. I've abstracted a simple example of my use case:
my expected result would look like this:
cart_id cart_total shopper_id items payment_id
1 345 34 [{"name":"egg","price:"39"}] AS34gsdVSWET4
2 54 45 [{"name":"biscut","price:"5"},{"name":"apple","price:"15"}] JFHERHJE$Y#$H
given a schema and data like:
carts:
id cart_total shopper_id
1 39 34
2 20 45
line_items:
id cart_id item_name item_price
1 1 egg 39
2 2 biscut 5
3 2 apple 15
payment:
id cart_id payment_id
1 1 AS34gsdVSWET4
2 2 JFHERHJE$Y#$H
How to get all cart list and get a list of carts for particular shopperId?

Here is my solution using json_build_object:
SELECT c.id AS cart_id,
c.cart_total,
c.shopper_id,
json_agg(
json_build_object(
'item_name', i.item_name,
'item_price', i.item_price::text
)
) AS items,
p.payment_id
FROM carts AS c
JOIN line_items AS i ON i.cart_id = c.id
JOIN payment AS p ON p.cart_id = c.id
GROUP BY c.id, c.cart_total, c.shopper_id, p.payment_id;
Are you sure you want the price as string?

Related

Get ID and name of each customer and the total sum (sum of quantity of products purchased)

I'm struggling with this JOIN + SUM query
What I have are these following tables (Client, orders, product)
client_id
name
1
Frank
2
Emile
3
Rose
4
Laura
5
Samuel
order_number
client_id
product_id
units_sold
1
4
1
11
2
3
2
8
3
5
3
18
4
4
4
19
5
3
5
12
product_id
description
price
1
Rice
26.10
2
Coffee
12.50
4
Sugar
13
5
Beans
5.40
3
Milk
30.00
What I'm trying to do is generate a query that shows the ID and name of each customer and the total sum (sum of quantity of products purchased).
So far, my logic without breaking it is this:
Select
c.client_id, c.first_name
from client c
INNER JOIN orders o on o.client_id = c.client_id
I want to add the SUM part to it but whenever I try it, the new query line doesn't work
If you just want the sum of quantity of products:
SELECT c.client_id, c.first_name, SUM(o.units_sold)
FROM
client c
INNER JOIN orders o ON o.client_id = c.client_id
GROUP BY c.client_id, c.first_name;
But if you also want to see the price:
SELECT c.client_id, c.first_name, SUM(o.units_sold),
SUM(o.units_sold*p.price)
FROM
client c
INNER JOIN orders o ON o.client_id = c.client_id
INNER JOIN products p ON o.product_id = p.product_id
GROUP BY c.client_id, c.first_name;

Get the Selected Minimum Price Row's Columns in SQL?

I have the following query,
SELECT * FROM Products P
OUTER APPLY
(
SELECT COALESCE(CP.NewPrice, CP.Price, 2147483647) AS MinimumChildPrice
--,SelectedMinimumPriceRow.Promotion
FROM Products CP
WHERE CP.ParentID = P.ID
) AS C
The problem is SelectedMinimumPriceRow.Promotion. I need to select this extra Promotion column of selected Minimum Price/NewPrice?
For example if I have,
ID ParentID Promotion Price NewPrice
----------------------------------------
1 NULL a 81 52
2 1 b 11 81
3 1 c 91 14
4 1 d 11 25
5 1 e 10 61
For ID=1, the minimum price will be 14. So, I need to select c as well.
You could simply replace your OUTER APPLY query with a TOP 1 query ordering by price, that will allow you to grab any column;
SELECT * FROM Products P
OUTER APPLY
(
SELECT TOP 1 COALESCE(CP.NewPrice, CP.Price, 2147483647) MinimumChildPrice,
promotion MinimumChildPricePromotion
FROM Products CP
WHERE CP.ParentID = P.ID
ORDER BY COALESCE(CP.NewPrice, CP.Price, 2147483647)
) AS C
An SQLfiddle to test with.

Order a table from values in another table

I have a table name 'Product'
ProductID ProductName
1 A
2 B
3 C
4 D
Another table named 'ViewedProduct'
ProductID Views UserID
2 2 55
2 1 40
1 10 40
1 50 127.0.1
3 51 127.0.1
for UserID 55 the expected result should be
ProductID ProductName
2 B
1 A
3 C
4 D
UserID 55 views should be at top and all other viewdproduct should come after it order by sum of their views. The product id 1 viewed by two different UserIDs thei total views is 60 that why it should come in second place. and product id 3 have 51 views it should come in third place.
Second Example :
ProductID Views UserID
2 2 55
3 3 55
2 1 40
1 10 40
1 50 127.0.1
3 51 127.0.1
4 50 127.0.1
Now for UserID 55 , it should first sort the result based on UserID 55. Then comes the rest result. Now the expected result i need should be
ProductID ProductName
3 C
2 B
1 A
4 D
Product ID 3 and 2 is not top because UserId 55 has viewed this 3 and 2 times respectively, the productid 1 is on the third place UserID 55 have not viewed this product. but their total views from viewed table is 60. And productID 4 comes last because userid 55 have not viewed this product but it total views from view table is 50. and so on..
-- Please comment if you need further explanation
I've edited the query to include the userID in the join clause as I believe this is necessary. see fiddle here: http://www.sqlfiddle.com/#!3/0ef18/1
SELECT
Product.ProductID,
Product.ProductName
FROM
Product
LEFT JOIN ViewedProduct ON Product.ProductID = ViewedProduct.ProductID AND ViewedProduct.UserID = #userID
WHERE
ViewedProduct.UserID IS NULL
OR ViewedProduct.UserID = #userID
ORDER BY
CASE WHEN ViewedProduct.Views IS NULL THEN 0 ELSE ViewedProduct.Views END DESC,
Product.ProductID
To order by the following:
UserViews,
AllViews,
ProductID
Use the following:
SELECT
Product.ProductID,
Product.ProductName
FROM
Product
LEFT JOIN ViewedProduct ON Product.ProductID = ViewedProduct.ProductID AND ViewedProduct.UserID = #userID
LEFT JOIN
(SELECT
ProductID,
SUM(Views) AllViews
FROM
ViewedProduct
GROUP BY
ProductID) TotalViewedProduct ON Product.ProductID = TotalViewedProduct.ProductID
ORDER BY
CASE WHEN ViewedProduct.Views IS NULL THEN 0 ELSE ViewedProduct.Views END DESC,
TotalViewedProduct.AllViews DESC,
Product.ProductID
It's not clear what you are asking here but if you want to return results from one of these tables and sort it by a column in another table just create a join between the main table and the table that holds the column you want to sort on. This will allow you to sort on columns in the joined table.
SELECT Product.ProductID, Product.ProductName
FROM Product
LEFT JOIN ViewedProduct ON Product.ProductID = ViewedProduct.ProductID
ORDER BY ViewedProduct.UserID
This however will not return the results you expect as UserID 55 has not viewed ProductID 1 or 3 and ordering will simply order ascending or descending.
If you want products viewed by UserID 55, you'll need a where clause:
SELECT Product.ProductID, Product.ProductName
FROM Product
LEFT JOIN ViewedProduct ON Product.ProductID = ViewedProduct.ProductID
WHERE ViewedProduct.UserID = 55
This will just return one row, unless there is more data than in your sample.
If you want to order by number of views try something like:
SELECT Product.ProductID, Product.ProductName
FROM Product
LEFT JOIN ViewedProduct ON Product.ProductID = ViewedProduct.ProductID
WHERE ViewedProduct.UserID = 55
ORDER BY ViewedProduct.Views

Access 2007 Left join and nulls

I ran the following simple query that looks like the example below:
(Briefly; one order has from 1 to N items and the foreign key is order_id in table items)
SELECT orders.*, items.*
FROM orders
LEFT JOIN
items ON orders.id= items.order_id
This shows something like:
order.id item.id
1 34
1 22
1 90
2 44
2 19
2 21
2 22
I want an output like:
order.id item.id
1 34
22
90
2 44
19
21
22
How to achieve that on Access 2007 ?
Thank you in advance,
Miloud B.
The only way I can think of doing this would be a self join with an aggregate. It will be easier/better handled in your application.
SELECT
IIF( i2.id = MIN(i.id), i.order_id, NULL) AS order_id,
i2.id AS item_id
FROM items i
INNER JOIN orders o
ON o.id= i.order_id
LEFT JOIN items i2
ON i2.order_id=i.order_id
GROUP BY i.order_id
ORDER BY i.order_id,
i2.id

Sql query to pull top 3 records with max votes in each category

Need some help with putting this query together. I'm using Mysql
I have two tables
Video - contains videos uploaded by users
video_id
user_id
category_id
Vote - contains a vote given by any user for a particular video
vote_id
video_id
user_id
I don't want to hardcode the categories in the query - the Categories are stored in the Category table which has category_id and category_name
I basically want a query that pulls the top 3 videos (ones with max votes) for each category.
Sample Data - Video Table
video_id | user_id | category_id
1 100 10
2 101 10
3 102 11
4 103 11
5 104 11
6 105 11
7 105 12
Sample Data - Vote Table
vote_id | video_id | user_id
11 3 105
12 3 102
13 3 111
14 3 121
15 4 200
16 4 201
17 1 222
Sample Data - Category Table
category_id | category_name
10 HipHop
11 Rap
12 Country
This is the type of problem that is trivial to solve with ranking functions. However, since MySQL does not yet support them, it makes it more difficult. In this design, I assumed that video_id was the primary key of the Video table.
Select video_id, user_id, category_id, vote_count, vote_rank
From (
Select VoteCounts.video_id, VoteCounts.user_id
, VoteCounts.category_id, VoteCounts.vote_count
, (
Select Count(*) + 1
From (
Select V1.video_id, V1.user_id, V1.category_id
, Count(vote_id) As vote_count
From Videos As V1
Left Join Votes As V2
On V2.video_id = V1.video_id
Group By V1.video_id, V1.user_id, V1.category_id
) As VoteCounts1
Where VoteCounts1.category_id = VoteCounts.category_id
And (
VoteCounts1.vote_count > VoteCounts.vote_count
Or (VoteCounts1.vote_count = VoteCounts.vote_count
And VoteCounts1.video_id < VoteCounts.video_id )
)
) As vote_rank
From (
Select V1.video_id, V1.user_id, V1.category_id
, Count(vote_id) As vote_count
From Videos As V1
Left Join Votes As V2
On V2.video_id = V1.video_id
Group By V1.video_id, V1.user_id, V1.category_id
) As VoteCounts
) As VoteRanks
Where VoteRanks.vote_rank <= 3
I don't know about MySQL, but here's how I would try to do it in SQL Server, and bringing perhaps some adjustments depending on the results which make sense.
select Category
, Film
, MAX(Votes) as Votes
from (
select TOP 3 c.category_name as Category
, vd.video_id as Film
, COUNT(vt.votes) as Votes
from Category c
inner join Video vd on vd.category_id = c.category_id
inner join Votes vt on vt.video_id = vd.video_id
where c.category_id = 10 -- HipHop
group by c.category_name
, vd.video_id
union
select TOP 3 c.category_name
, vd.video_id
, COUNT(vt.votes) as Votes
from Category c
inner join Video vd on vd.category_id = c.category_id
inner join Votes vt on vt.video_id = vd.video_id
where c.category_id = 11 -- Rap
group by c.category_name
, vd.video_id
-- You need as much UNION as there are categories.
) Top3VotesPerCategory
group by Category
, Film
Depending whether you accepte downvotes on a film, one could register a negative vote a film, and this wouldn't mean that he recommends this video. If you do support such functional feature, then you might want to consider a conditional SUM(), instead of a COUNT(), for the number of votes, so that negative votes would be considered and would decrease its overall points in regards of votes.