SQL multiple rows in single row - php

Hi everyone I have this table and I want to show result in single row group by client id and show availability for stock 1 and 2.
Here is my table
id client stock material quantity availability date
62 56 1 0 0 100 2017-12-16 23:55:01
63 56 2 0 0 900 2017-12-16 23:55:01
64 56 1 100 -20 80 2017-12-16 23:55:20
65 56 1 80 100 180 2017-12-16 23:56:06
66 56 1 180 200 380 2017-12-16 23:56:21
67 56 1 380 500 880 2017-12-16 23:58:11
68 56 1 880 -580 300 2017-12-16 23:58:38
69 56 2 900 -90 810 2017-12-17 23:59:18
Outcome I want is get result from last date, group by client id and combine stock 1 and stock 2 to single row
client availability1 availability2
56 300 810
I try this query
SELECT
historys.id
,(CASE WHEN historys.stock = 1 THEN availability END) AS availability1
,(CASE WHEN historys.stock = 2 THEN availability END) AS availability2
FROM historys
GROUP BY historys.client
ORDER by historys.id
The result is
id availability1 availability2
56 NULL 810
I will be grateful if someone help me. Thanks.

You need to filter to the right rows before the aggregation. Here is one method:
SELECT h.client,
MAX(CASE WHEN h.stock = 1 THEN h.availability END) AS availability1
MAX(CASE WHEN h.stock = 2 THEN h.availability END) AS availability2
FROM historys h
WHERE h.date = (SELECT MAX(h2.date) FROM historys h2 WHERE h2.stock = h.stock)
GROUP BY h.client

Use a union
SELECT
client, max(availability1) AS availability1, max(availability2) AS availability2
FROM
(
SELECT
client
,availability AS availability1
,0 AS availability2
FROM historys hist
WHERE id = (select max(id) from historys where client = hist.client and stock = 1)
UNION ALL
SELECT
client
,0 AS availability1
,availability AS availability2
FROM historys hist2
WHERE id = (select max(id) from historys where client = hist2.client and stock = 2)
) a
GROUP by client
ORDER by client

The NULL you have is because your selecting data from lines of historys that cannot contain the availability for stock = 1 and stock = 2 at the same time.
You can bypass that using historys both times like above :
In the subrequest (as d) we get max dates by client, then we join history two times to get both availability in a row.
select d.client,
h1.availability availability1,
h2.availability availability2
from
(
select client,
max(case when stock = 1 then date end) d1,
max(case when stock = 2 then date end) d2
from historys
group by client
) d
join historys h1 on (h1.stock = 1
and h1.date = d.d1
and h1.client = d.client)
join historys h2 on (h2.stock = 2
and h2.date = d.d2
and h2.client = d.client)
You can find an SQLFiddle here : http://sqlfiddle.com/#!9/d1ea4/5
Thanks

This may help you :
SELECT T.client client ,
SUM(CASE WHEN T.Stock = 1 THEN T.availability
END) availability1 ,
SUM(CASE WHEN T.Stock = 2 THEN T.availability
END) availability2
FROM historys T
INNER JOIN ( SELECT MAX(date) Max_Date ,
stock
FROM historys
GROUP BY stock
) T1 ON T1.Max_Date = T.date
AND T1.stock = T.stock
GROUP BY T.client

Depending on your MySQL version, you should be able to do this with a window function & a common table expression:
-- Build derived src table with "latest date" rows
WITH src AS (
SELECT id, client, stock, material, quantity, availability, date
FROM (
SELECT
id, client, stock, material, quantity, availability, date,
ROW_NUMBER() OVER(PARTITION BY client, stock ORDER BY date DESC) ClientStockRank -- For each (client, stock) pair, rank each row by date
FROM historys a
) src
WHERE ClientStockRank = 1 -- Only get rows with "latest date"
)
SELECT
client,
MAX(CASE WHEN stock = 1 THEN availability ELSE NULL) END AS Availability1, -- Get "max" value for "stock 1"
MAX(CASE WHEN stock = 2 THEN availability ELSE NULL) END AS Availability2 -- Get "max" value for "stock 2"
FROM src
GROUP BY client
I think you need MySQL 8+. If not, there should be other ways to do it too. Let me know if that works.
Update
Actually, this should also suffice:
SELECT DISTINCT
client,
MAX(CASE WHEN stock = 1 THEN availability ELSE NULL) OVER(PARTITION BY client ORDER BY date DESC) AS Availability1,
MAX(CASE WHEN stock = 2 THEN availability ELSE NULL) OVER(PARTITION BY client ORDER BY date DESC) AS availability2
FROM historys

Related

Reliable way of getting the max value of a column for each unique user id while meeting other criteria

I'm trying to bulk-get all of the latest payments for a group of user ids, but I've been struggling with getting all of them under a unified query
I went with:
SELECT t1.*
FROM movements t1 LEFT JOIN movements t2
ON (t1.user = t2.user AND t1.id < t2.id)
WHERE t2.id IS NULL
AND t1.user IN ({$ids}) AND t1.type='payment' AND t1.concept!='4' AND t1.confirmed
...which worked to an extent, but some entries were being left out. I extended it to
ON (t1.user = t2.user AND t1.id < t2.id)
WHERE t2.id IS NULL AND t2.date IS NULL
and that yielded more results, but some of them weren't being selected still.
Here are two samples where the query will not yield anything
id user concept date type confirmed
---------------------------------------------------------------------------------
29755 107 3 2022-06-12 00:01:00 payment 1
31257 107 3 2022-07-12 00:00:00 payment 1
32189 107 3 2022-08-12 00:00:00 payment 1
32460 107 COMISSION BALANCE 2022-08-23 10:50:50 comission
id user concept date type confirmed
---------------------------------------------------------------------------------
27298 8408 3 2022-03-11 08:44:53 40 payment
28446 8408 3 2022-03-11 00:01:00 40 payment 1
28447 8408 3 2022-04-19 17:22:42 40 payment
Using the "crude" alternative does, obviously.
SELECT * FROM movements WHERE user=107 AND (etc) ORDER BY id DESC LIMIT 1
id user concept date type confirmed
---------------------------------------------------------------------------------
32189 107 3 2022-08-12 00:00:00 payment 1
Since I have the ids, I could simply do a foreach for every $user and make a million individual calls, but I'd rather avoid that.
I can tell there's something off with the "topmost" rows not meeting the full criteria, but I have no clue on how to tell SQL to get me the ones that do.
How could I achieve this in a clean way?
To select the row with the highest id for each user that matches the additional criteria, even if there's a row with a higher id that doesn't match the additional criteria, you need to add to your on clause:
and t2.type='payment' AND t2.concept!='4' AND t2.confirmed
You may also try the following:
Select T.id, T.user, T.concept, T.date, T.type, T.confirmed
From
movements T
Join
(
Select MAX(id) lastid, user
From movements
Where user IN (107, 8408) And type='payment' And concept!='4' And confirmed
Group By user
) D
On T.id = D.lastid
See a demo.
select id
,user
,concept
,date
,type
,confirmed
from (
select *
,row_number() over(partition by user order by id desc) as rn
from t
where confirmed = 1
) t
where rn = 1
id
user
concept
date
type
confirmed
32189
107
3
2022-08-12 00:00:00
payment
1
28446
8408
3
2022-03-11 00:01:00 40
payment
1
Fiddle

How get recorde from database with limit SUM value column

I have table with some customer invoices. I need return some record that SUM(price) less than 200 dollar, this mean return record until total or sum invoice price < 200$.
Sometimes i want return latest record back and sometimes from beginning.
I use MYSQL database and php.
table
-----------------------
id | customerId | price
1 20 15
2 15 10
3 65 42
4 44 12
5 23 78
6 11 66
7 16 95
8 33 48
I would recommend variables for calculating the cumulative sum:
select t.*
from (select t.*, (#s := #s + price) as sum_price
from t cross join
(select #s := 0) params
order by id
) t
where sum_price < 200;
let HAVING filter your results to those < 200:
SELECT SUM(price) as total, customerId
FROM table
GROUP BY customerId
HAVING total < 200
Edit
So you do not need the customer, but all the records for him: then use query above as a subquery:
SELECT maintable.Id, maintable.customerId, maintable.price
FROM table AS maintable
JOIN (
SELECT customerId
FROM table
GROUP BY customerId
HAVING SUM(price) < 200
) subq ON subq.customerId = maintable.customerId
SELECT
t.id,
t.customerid,
t.price,
(SELECT
sum(price) FROM Table1
WHERE Id <= t.id) 'CalculatedTotal'
FROM Table t
HAVING CalculatedTotal <=200
this query calculates and return rows having total less than 200.

Get sum of min values in each group from Mysql

I am trying to retrieve the minimum price of some models.
Each model belongs to a certain group which belongs to a product.
I have the following tables:
Product
model_id product_id price
1 1 100
2 1 120
3 1 100
4 1 200
5 1 250
10 1 20
11 1 50
12 1 50
Product Overview
model_id product_id group_id
1 1 A
2 1 A
3 1 A
4 1 A
5 1 A
10 1 B
11 1 B
12 1 B
Product Group Optional
group_id product_id
B 1
Some groups could be optional, which means price will be zero unless the member wants to choose otherwise.
So in the example above, I want to get the sum of minimum price from each group.
We have two groups, group A and group B.
Group A minimum price value is 100 (model_id 1 and 3)
Group B minimum price value is 20 (model_id 10) but because Group B is optional then that means minimum price value is 0.
Overall sum of min values: 100 (Group A) + 0 (Group B) = 100
My code so far:
SELECT po.group_id,
CASE WHEN
((SELECT COUNT(*) FROM product_group_optional pgo
WHERE po.group_id = group_id AND po.product_id = 1 AND po.product_id = product_id) >= 1)
THEN SUM(0)
ELSE SUM(p.price)
END AS sum_price
FROM product_overview po, product p
WHERE po.product_id = 1
AND po.model_id = p.model_id
AND p.price = (
SELECT MIN(p2.price)
FROM product p2, product_overview po2
WHERE po2.product_id = 1 AND po2.group_id = po.group_id
AND po2.model_id = p2.model_id
)
GROUP BY po.group_id
The output:
group_id sum_price
A 200
B 0
The problem is that I get 200 for Group A but it should be 100.
There are 2 models with min value 100, model 1 and 3. And I assume these are sum together = 100 + 100 = 200.
Issue a) But I want to just take the min value, no matter how many times this value exists.
Issue b) Also, I am trying to get the SUM of those two output SUM of Group A and Group B.
But I am not sure how to do it.
I want it to be done in this query.
Desired output
Sum of all groups
100
Can anyone lead me to the right direction please?
You can use the following query:
SELECT SUM(min_price)
FROM (
SELECT po.group_id,
MIN(CASE WHEN pgo.group_id IS NULL THEN price ELSE 0 END) AS min_price
FROM Product AS p
INNER JOIN Product_overview AS po
ON p.product_id = po.product_id AND p.model_id = po.model_id
LEFT JOIN Product_group_optional AS pgo ON po.group_id = pgo.group_id
GROUP BY po.group_id) AS t
I'm not sure that I understand the keys of your tables, and the problem as well.
There is few questions.
a) The answer should be 120?
b) If the Product has no price, the is price null?
c) If there is a Product in a group with null price and others with price, should it be counted as 0?
Here is how you could get the sum of the lower prices of each group, ignoring the product_group_optional for while:
SELECT t2.group_id, sum(t2.new_price)
FROM
(
SELECT t.group_id, t.new_price
FROM
(
SELECT po.group_id, if(ifnull(pgo.product_id, true), p.price, 0) as new_price
FROM product p, product_overview po
LEFT JOIN product_group_optional pgo ON po.group_id = pgo.group_id
WHERE p.model_id = po.model_id
ORDER by po.group_id, new_price
) t
GROUP BY t.group_id
) t2

sql Calculate boys and girls present and absent

Can't manage to come up with a correct query to compute the total no of students
I have three tables:
student - contains student profile either male or female
2.student_attendance - contains attendance details for whether a student was present a either "0" or "1"
attendance - contains all session details where by a one session can be attended by a number of students.
I need to calculate the number of boys/girls in present or absent for a session.
my major headache is to interpreate these logic to sql
if(in_attendace =1) then
sum the number of boys as boys_present
sum the number of girls as girls_present
else
sum the number of boys as boys_absent
sum the number of girls as girls_absent
# MY closest sql is its not working :(
select
case when a.in_attendance = 1 then
SUM(CASE b.gender when 1 then 1 else 0 end ) as male_present,
SUM(CASE b.gender when 2 then 1 else 0 end ) as female_present,
ELSE
SUM(CASE b.gender when 1 then 1 else 0 end ) as male_absent,
SUM(CASE b.gender when 2 then 1 else 0 end ) as female_absent
END
from attendance_student as a inner join student as b on a.student_id = b.id where a.session_details_id = 38
Well, you are not very far from the solution, you just need to separate them into different columns(I assume that's what you want) :
select COUNT(CASE WHEN a.in_attendance = 1 and b.gender = 1 then 1 END) as male_present,
COUNT(CASE WHEN a.in_attendance = 1 and b.gender = 2 then 1 END) as female_present,
COUNT(CASE WHEN a.in_attendance = 0 and b.gender = 1 then 1 END) as male_absent,
COUNT(CASE WHEN a.in_attendance = 0 and b.gender = 2 then 1 END) as female_absent
FROM attendance_student a
INNER JOIN student b
ON a.student_id = b.id
WHERE a.session_details_id = 38

How to left join selecting value with the highest user_id

well this question is related to my previous question How to left join 2 tables with SUM() and MAX() grouped by date
what i changed is i added user_id column (auto incremented) and want to select value with highest user_id per date
i have table loadhistory ( wanted to "select only value with highest user_id per date" group by and order by date DESC.)
so in this case, i want to select 150 for 2015-02-27 since it has the highest user_id in that date and 50 for 2015-02-28
| user_id | customer_id | date | bal |
1 1 2015-02-27 100
2 1 2015-02-27 150
3 1 2015-02-28 150
4 1 2015-02-28 50
and table transactionrecord (want to sum up values per date using SUM(bal) group by and order by date DESC)
| user_id |customer_id | date | bal |
1 1 2015-02-27 50
2 1 2015-02-27 20
3 1 2015-02-28 10
And i want to join the 2 tables which would look like this:
| date | balance | amount paid |
2015-02-28 50 10
2015-02-27 150 70
this is the code so far (i used the code i got from my previous question and edited it here in my new question hoping to arrive desired result but did not)
SELECT a.customer_id, a.date, (b.bal AS bal WHERE b.user_id = MAX(b.user_id) , a.paid
FROM (
SELECT customer_id, date, SUM(bal) AS paid
FROM transactionrecord
GROUP BY customer_id, date
) AS a LEFT JOIN loadhistory AS b
ON a.customer_id = b.customer_id AND a.date = b.date
WHERE a.customer_id = 1
GROUP BY a.customer_id, a.date, a.paid
ORDER BY a.date DESC
help please. thanks in advance
MySQL use the first row by group by, so you must order it before you can use group by like this:
SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date
So you can use the following query as solution:
SELECT h.date,h.bal as balance, t.amount as 'amount paid' FROM
(SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date) as h
JOIN
(SELECT SUM(bal) as amount, customer_id, date FROM `transactionrecord` GROUP BY date) as t
ON h.date = t.date AND h.customer_id = t.customer_id
ORDER BY date DESC

Categories