JOIN three tables together and limit a row of thirst table - PDO - mysql

I have three tables :
table1
id title name user token
1 supermarket big market user1.example#gmail.com aaaaa
2 hear styler stylist user2.example#gmail.com bbbbb
table2
id country city user token
1 USA New York user1.example#gmail.com aaaaa
2 UK London user2.example#gmail.com bbbbb
table3
id url user token
1 uploadPic/image1.jpg user1.example#gmail.com aaaaa
2 uploadPic/image2.jpg user1.example#gmail.com aaaaa
3 uploadPic/image3.jpg user2.example#gmail.com bbbbb
4 uploadPic/image4.jpg user2.example#gmail.com bbbbb
5 uploadPic/image5.jpg user2.example#gmail.com bbbbb
6 uploadPic/image6.jpg user2.example#gmail.com bbbbb
and I used INNER JOIN to merge tables together:
$response['info'] = array();
$sql = "SELECT * FROM `table1` as tb1
INNER JOIN `table2` as tb2 ON tb1.token = tb2.token
INNER JOIN table3 as tb3 ON tb3.token = tb1.token
ORDER BY tb1.id DESC";
$run = $connect->prepare($sql);
$run->execute();
$record = $run->fetchAll(PDO::FETCH_ASSOC);
$response['info'] = $recordAdvert;
echo json_encode($response,JSON_PRETTY_PRINT);
and the result is :
{
"info": [
{
"id": "6",
"title": "superMarket",
"name": "big market",
"user": "user2.example#gmail.com",
"token": "bbbbb",
"country": "UK",
"city": "London",
"url": "uploadPic/image2.jpg"
},
{
"id": "5",
"title": "superMarket",
"name": "big market",
"user": "user2.example#gmail.com",
"token": "bbbbb",
"country": "UK",
"city": "London",
"url": "uploadPic/image1.jpg"
},
{
"id": "4",
"title": "superMarket",
"name": "big market",
"user": "user2.example#gmail.com",
"token": "bbbbb",
"country": "UK",
"city": "London",
"url": "uploadPic/image6.jpg"
},
{
"id": "3",
"title": "hear styler",
"name": "stylist",
"user": "user1.example#gmail.com",
"token": "aaaaa",
"country": "USA",
"city": "NEW YORK",
"url": "uploadPic/image5.jpg"
},
{
"id": "2",
"title": "hear styler",
"name": "stylist",
"user": "user1.example#gmail.com",
"token": "aaaaa",
"country": "USA",
"city": "NEW YORK",
"url": "uploadPic/image4.jpg"
},
{
"id": "1",
"title": "hear styler",
"name": "stylist",
"user": "user1.example#gmail.com",
"token": "aaaaa",
"country": "USA",
"city": "NEW YORK",
"url": "uploadPic/image3.jpg"
}
]
}
but this is not that i want. there are must be just two rows because i have just two user and two token so i want to limit image to 1 but i can't due to when i used limit in image table to just limit image to one for both of users it limit total table.
the result must be something like this:
{
"advert": [
{
"id": "2",
"title": "hear styler",
"name": "stylist",
"user": "user2.example#gmail.com",
"token": "bbbbb",
"country": "UK",
"city": "London",
"url": "uploadPic/image3.jpg"
},
{
"id": "1",
"title": "supermarket",
"name": "big market",
"user": "user1.example#gmail.com",
"token": "aaaaa",
"country": "USA",
"city": "New york",
"url": "uploadPic/image1.jpg"
}
]
}
I edit the select query like this :
$sql = "SELECT * FROM `table1` as tb1
INNER JOIN `table2` as tb2 ON tb1.token = tb2.token
INNER JOIN (SELECT * FROM table3 LIMIT 1) as tb3 ON tb3.token = tb1.token
ORDER BY tb1.id DESC";
but it limit the total row not just table 3 row.
{
"info": [
{
"id": "6",
"title": "superMarket",
"name": "big market",
"user": "user2.example#gmail.com",
"token": "bbbbb",
"country": "UK",
"city": "London",
"url": "uploadPic/image2.jpg"
}
]
}

You can use a correlated subquery that use LIMIT to get just one URL per user.
SELECT t1.id,
t1.title,
t1.name,
t1.user,
t1.token,
t1.country,
t2.city,
(SELECT t3.url
FROM table3 t3
WHERE t3.token = t1.token
ORDER BY t3.id DESC
LIMIT 1) url
FROM table1 t1
INNER JOIN table2 t2
ON t2.token = t1.token
ORDER BY t1.id DESC;

Related

Parsing JSON data with LOOP

I am parsing data from JSON with UNION ALL, but I need repeated it more times. Try to use LOOP, but it doesn't works. :(
I need to parse every element from array in JSON to rows from statements.
I change the number of element in: statements::json->0 and than UNION the data.
The CODE I want to replace with some LOOP:
(
SELECT
execution_entry_id,
account,
trim('"' FROM (account::json->'startBalance')::text) AS startBalance,
trim('"' FROM (account::json->'endBalance')::text) AS endBalance
FROM (
SELECT
execution_entry_id,
statements::json->0 AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a
) b WHERE account IS NOT NULL
)
UNION ALL
(
SELECT
execution_entry_id,
account,
trim('"' FROM (account::json->'startBalance')::text) AS startBalance,
trim('"' FROM (account::json->'endBalance')::text) AS endBalance
FROM (
SELECT
execution_entry_id,
statements::json->1 AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a
) b WHERE account IS NOT NULL
)
I try to use:
do $$
declare
counter integer := 0;
begin
while counter < 10 loop
SELECT
execution_entry_id,
statements::json->counter AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a;
counter := counter + 1;
end loop;
end$$;
and it ends with error:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
1 statement failed.
It's possible to get data with LOOP or how instead of 10x UNION ALL, please?
Thanks.
If you are use statements::json->0, so you have Json array in your Json string. For splitting array elements from Json array, you can use jsonb_array_elements_text function on PostgreSQL, this function will be extracting your array elements like as rows. For do this you don't need using loop. For example:
with tbl as (
select
'[
{
"employee_id": 2,
"full_name": "Megan Berry",
"manager_id": 1
},
{
"employee_id": 3,
"full_name": "Sarah Berry",
"manager_id": 1
},
{
"employee_id": 4,
"full_name": "Zoe Black",
"manager_id": 1
},
{
"employee_id": 5,
"full_name": "Tim James",
"manager_id": 1
},
{
"employee_id": 6,
"full_name": "Bella Tucker",
"manager_id": 2
}
]'::jsonb as jsondata
)
select jsondata->1 from tbl;
Result:
{"full_name": "Sarah Berry", "manager_id": 1, "employee_id": 3}
This is getting second element of Json array.
But you need all elements, for getting all elements and extracting key values use this syntax:
with table1 as (
select
'[
{
"employee_id": 2,
"full_name": "Megan Berry",
"manager_id": 1
},
{
"employee_id": 3,
"full_name": "Sarah Berry",
"manager_id": 1
},
{
"employee_id": 4,
"full_name": "Zoe Black",
"manager_id": 1
},
{
"employee_id": 5,
"full_name": "Tim James",
"manager_id": 1
},
{
"employee_id": 6,
"full_name": "Bella Tucker",
"manager_id": 2
}
]'::jsonb as jsondata
)
select
tb1.a1->>'full_name' as fullname,
tb1.a1->>'manager_id' as managerid,
tb1.a1->>'employee_id' as employeeid
from (
select jsonb_array_elements_text(jsondata)::jsonb as a1 from table1
) tb1
Result:
fullname managerid employeeid
Megan Berry 1 2
Sarah Berry 1 3
Zoe Black 1 4
Tim James 1 5
Bella Tucker 2 6

How to join two tables in mysql with matching data and group in array

I need to join two tables (products and orders) in mysql with a matching data (order id) and different data (products ids) that should be in a array.
I tried with a simple JOIN but data are not being grouped. Does anyone can give me a light?
SELECT Order.id, Order.userId, Product.id AS productsIds
FROM Orders AS Order
JOIN Products AS Product
ON Order.id = Product.orderId
Result:
[
{
"id": 1,
"userId": 1,
"productsIds": 2
},
{
"id": 1,
"userId": 1,
"productsIds": 5
},
{
"id": 1,
"userId": 1,
"productsIds": 6
},
{
"id": 3,
"userId": 2,
"productsIds": 4
},
{
"id": 2,
"userId": 3,
"productsIds": 3
}
]
Expected:
[
{
"id": 1,
"userId": 1,
"productsIds": [2, 5, 6]
},
{
"id": 3,
"userId": 2,
"productsIds": 4
},
{
"id": 2,
"userId": 3,
"productsIds": 3
}
]
You need to use GROUP BY clause and GROUP_CONCAT in select statement, will give the expected result.
Following is the query.
SELECT Order.id, Order.userId, GROUP_CONCAT(Product.id) AS productsIds FROM Orders AS Order JOIN Products AS Product ON Order.id = Product.orderId GROUP BY Order.id, Order.userId ;

mysql jsonarrayagg for comma seperated id

i have two table casetemp and medicication_master
patient has fields
id
name
age
sex
medicineid
1
xyz
23
M
1,2
2
abc
20
f
3
medicine has fields
id
medname
desc
1
crosin
fever tab
2
etzaa
shampoo
3
zaanta
painkiller
i want the mysql left join output as following :
[{
"id":"1",
"name":"xyz",
"age":"23",
"sex":"M",
"medicine_id":"1,2",
"medicine_Data":[
{
"id":"1"
"medname":"crosin",
"desc":"fever tab"
},
{
"id":"2"
"medname":"etzaa",
"desc":"shampoo"
}]
},
{
"id":"2",
"name":"abc",
"age":"20",
"sex":"F",
"medicine_id":"3",
"medicine_Data":[{
"id":"3"
"medname":"zaanta",
"desc":"pain killer"
}]
}]
the query i used is
SELECT json_object(
'id', b.id,
'name',b.name,
'age',b.age,
'sex',b.sex,
'medicine_id',b.medicine_id,
'medicine_data', json_arrayagg(json_object(
'id', pt.id,
'medname', pt.medname,
'desc',pt.desc,
))
)
FROM patient b LEFT JOIN medication_master pt
ON b.medicine_id = pt.id
where b.id=1
GROUP BY b.id;
thanks in advance

Display value in column although it is excluded by a WHERE condition over another column

DB-Fiddle
CREATE TABLE operations (
id int auto_increment primary key,
orderID VARCHAR(255),
itemID VARCHAR(255),
event_date DATE,
order_volume INT,
shipped_volume INT
);
INSERT INTO operations
(itemID, orderID, event_date, order_volume, shipped_volume
)
VALUES
("Item_01", "Order_XYZ", "2020-05-01", "600", "0"),
("Item_01", "Order_XYZ", "2020-05-18", "0", "315"),
("Item_01", "Order_XYZ", "2020-05-19", "0", "100"),
("Item_01", "Order_MTE", "2020-08-15", "400", "0"),
("Item_01", "Order_OBD", "2020-08-21", "500", "0"),
("Item_01", "Order_OBD", "2020-11-17", "0", "380"),
("Item_02", "Order_TLP", "2020-02-02", "500", "0"),
("Item_02", "Order_TLP", "2020-02-10", "0", "175"),
("Item_02", "Order_ADF", "2020-03-27", "100", "0"),
("Item_03", "Order_BBI", "2020-03-12", "700", "0"),
("Item_04", "Order_DXR", "2020-12-09", "260", "0"),
("Item_04", "Order_DXR", "2020-12-15", "0", "110"),
("Item_04", "Order_DXR", "2020-12-15", "0", "60"),
("Item_04", "Order_FGU", "2020-12-15", "0", "80");
Expected Result:
itemID | orderID | sum(order_volume) | sum(shipped_volume)
--------------|---------------|--------------------------|---------------------------------------
Item_04 | Order_DXR | 260 | 250
Item_02 | Order_TLP | 500 | 175
Item_01 | Order_XYZ | 600 | 415
Item_01 | Order_OBD | 500 | 380
In the result above I want to list all itemID and orderID that have shipped_volume > 0.
Therefore, I went with this query:
SELECT
itemID,
orderID,
sum(order_volume),
sum(shipped_volume)
FROM operations
WHERE shipped_volume > 0
GROUP BY 1,2;
It gives me almost the result I am looking for.
The only issue I have is that it puts a 0 for all rows in column sum(order_volume) which is caused by the WHERE condition.
What do I need to change so it also displays the sum(order_volume) of all items and orders that have a shipped_volume > 0?
Simply us HAVING instead of where, this example is the perfect exmaple of how to use HAVING, think of it as applying the filter AFTER calculating the group results!
SELECT
itemID,
orderID,
SUM(order_volume),
SUM(shipped_volume)
FROM operations
GROUP BY itemID, orderID
HAVING SUM(shipped_volume) > 0;
Your fiddle updated
More reading on HAVING specifically for MySQL:
https://www.mysqltutorial.org/mysql-having.aspx
NOTE: In this example I have used explicit column name references instead of ordinals, there are many supporting arguments for this, I go with "it is more expressive and harder to get wrong".
Ordinals in GROUP BY is not supported in all db engines or versions.
https://stackoverflow.com/a/45569726/1690217
This may be what you are looking for if i understand correctly :
SELECT
itemID,
orderID,
sum(order_volume),
sum(shipped_volume)
FROM operations
GROUP BY 1,2
HAVING sum(shipped_volume) > 0;
FIND A DEMO HERE
You have to use group by subquery:
SELECT
itemID,
orderID,
(select sum(order_volume) from operations o where o.orderid = op.orderid) as order_volumne ,
sum(shipped_volume)
FROM operations op
WHERE shipped_volume > 0
GROUP BY 1,2;

Getting duplicated sql result

I have 3 tables: Users, Articles and Votes
| Users | | Articles | | Votes |
| id | | id | | userId |
| name | | title | | articleId |
| email | | userId | | type |
I want to get users list with Count voteup and Count votedown for each one.
I'm testing this query:
SELECT u.id,u.name,u.email,
(SELECT COUNT(*) FROM votes as v WHERE v.type=1 AND v.articleId IN
(SELECT a.id From articles as a WHERE a.userId = u.id) ) AS totalvoteup,
(SELECT COUNT(*) FROM votes as v WHERE v.type=0 AND v.articleId IN
(SELECT a.id From articles as a WHERE a.userId = u.id) ) AS totalvotedown
FROM users as u
I have the list I want when I test it via phpmyadmin(the results number matches with the number of users in the table), but when I try to get through Node server(from AngularJs or Postman) I'm getting duplicate results:
{
"users": [
[
{
"id": 1,
"name": "John Lennon",
"email": "johnlennon#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 2,
"name": "John Lennon 2",
"email": "johnlennon2#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 3,
"name": "John Lennon 3",
"email": "johnlennon3#gmail.com",
"totalvoteup": 0,
"totalvotedown": 1
},
{
"id": 4,
"name": "John Lennon 4",
"email": "johnlennon4#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
}
],
[
{
"id": 1,
"name": "John Lennon 1",
"email": "johnlennon1#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 2,
"name": "John Lennon 2",
"email": "johnlennon2#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 3,
"name": "John Lennon 3",
"email": "johnlennon3#gmail.com",
"totalvoteup": 0,
"totalvotedown": 1
},
{
"id": 4,
"name": "John Lennon 4",
"email": "johnlennon4#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
}
]
]
}
Any ideas how to resolve this?
Am not a Java guy and am not sure about the problem in question, but the query can be written in better way
SELECT DISTINCT u.id,
u.name,
u.email,
coalesce(totalvoteup,0) as totalvoteup,
coalesce(totalvotedown,0) as totalvotedown
FROM users AS u
LEFT JOIN (SELECT DISTINCT id,userId FROM articles) a
ON a.userId = u.id
LEFT JOIN (SELECT Count(CASE WHEN v.type = 1 THEN 1 END) AS totalvoteup,
Count(CASE WHEN v.type = 0 THEN 1 END) AS totalvotedown,
v.articleId
FROM votes v
GROUP BY v.articleId) v
ON a.id = v.articleId
May not help you to solve the problem just thought of sharing since I like query optimization