How to combine WHERE and HAVING in a MySQL query - mysql

How do I write a query for MySQL that would filter out where total pay is less than zero.
I have tried different commands using 'HAVING' but I can't figure out the proper syntax.
This is my command:
SELECT tickets.idno, tickets.PAY, tickets.MATERIAL
FROM tickets
WHERE ( tickets.PAY <> 0 OR tickets.MATERIAL <> 0 ) AND tickets.DISTRICT = 'HO' AND tickets.DATEPAID IS NULL
ORDER BY tickets.NAME
with this result set:
HO0045 -140 0
HO2203 -45 0
HO2411 -5 0
HO2411 20 0
HO3448 -156 0
HO2519 2000 0
HO0075 -300 0
HO1669 -55 0
HO2666 -200 0
HO2666 -200 0
HO3447 -400 0
HO3447 400 0
This is result I get now, but it needs to eliminate those records where the total pay + total material for all rows for a given IDNO are less than zero. For example; all the rows for HO2411 and HO2519 should appear but not the rest.

HAVING is meant to filter out whole groups, but to do that, you need to reduce the query with GROUP BY. But unfortunately you can't reverse the grouping operation after filtering groups.
So you have to do a subquery to filter the groups, then JOIN that result back to the table to find the rows with the corresponding idno:
SELECT t.idno, t.PAY, t.MATERIAL
FROM tickets AS t
INNER JOIN (
SELECT idno, SUM(PAY) AS PAY, SUM(MATERIAL) AS MATERIAL
FROM tickets
GROUP BY idno
HAVING PAY + MATERIAL < 0
) AS total USING (idno)
WHERE t.DISTRICT = 'HO' AND t.DATEPAID IS NULL
ORDER BY t.NAME;

Related

How to Update Subtract value to the existing Table Using Mysql

I have three columns in my table the name of the column is date_time,total,sub and i want to update the existing sub column by subtracting previous row total value and next row total value by order of date_time column using mysql.I don't know how to achieve this result.The data are given below
date_time total sub
2019-03-07 12:32:10 50 0
2019-03-07 12:34:22 60 0
2019-03-07 12:37:17 75 0
2019-03-07 12:44:10 100 0
i want to update the above record as
date_time total sub
2019-03-07 12:32:10 50 0
2019-03-07 12:34:22 60 10
2019-03-07 12:37:17 75 15
2019-03-07 12:44:10 100 25
Please guide me to achieve this result
Try this code
update your_table_name U left join
(select
(CASE
WHEN #pev_row_value = 0 THEN 0
ELSE (`your_table_name `.total - #pev_row_value)
END) as sub_result,
(#pev_row_value := `your_table_name `.total ) as current_row,
users.*
FROM `your_table_name ` cross join
(select #pev_row_value := 0) params
order by date_time asc) A on U.date_time=A.date_time
set U.sub = A.sub_result
Hopefully it will be work fine. In CASE WHEN ELSE statement set the sub_result for update and pev_row_value stored the value of previous row of total column value. Then whole regenerated value worked as a table which new name is A and update the final value with join the new table A.
Thanks

MySQL SELECT record immediately prior to range and select range

I have a MySQL table of states for three things, a,b and c
id a b c time
--------------------------
1 0 1 1 78
2 1 1 0 89
3 1 0 0 105
4 0 0 0 107
5 1 0 1 122
6 0 0 1 134
7 0 1 0 167
8 1 1 1 168
9 0 1 0 177
10 0 0 0 180
As an example, the bounds of time are chosen by the user as time>100
AND time<170
But I need to know the value of ‘a’ immediately prior to the 1st returned record. (where id=2)
I’m trying to find the most efficient way of creating this query, without resorting to 2 separate queries.
SELECT a, time FROM states WHERE time<100 order by time DESC limit 1
AND
SELECT a, time FROM states WHERE time>100 AND time<170 ORDER BY time ASC
To return a result set of ...
a time
1 89
1 105
0 107
1 122
0 134
0 167
0 168
Any advice would be gratefully received, thanks!
One method uses LEAD():
SELECT a, time
FROM (SELECT s.*, LEAD(time) OVER (ORDER BY time) as next_time
FROM states s
) s
WHERE next_time > 100 AND time < 170;
You can also use:
select s.*
from states s
where s.time >= (select s2.time from states s2 where s2.time <= 100 order by s2.time desc limit 1) and
s.time < 170;
This, alas, doesn't work when the subquery returns no values. That can be fixed, but it complicates the query.
However, your solution is actually fine (with union all):
(SELECT a, time
FROM states
WHERE time <= 100
ORDER BY time DESC
LIMIT 1
) UNION ALL
(SELECT a, time
FROM states
WHERE time > 100 AND time < 170
)
ORDER BY time ASC;
From a performance perspective, this should be okay if you have an index on time. This also readily handles the problem when there are no values 100 or less.

MySQL: Get all rows if specific value reached after date

In MySQL, I want to find all the rows where the value of a column went to 0 after a specific date.
So, given the data:
cat value date
a 95 2015-09-01
a 78 2015-10-01
a 0 2015-11-01
a 0 2015-12-01
b 129 2015-09-01
b 230 2015-10-01
b 201 2015-11-01
b 140 2015-12-01
In this case, I want to run a query that asks:
Which categories have 0 value after 10/1/2015 and had a positive value before 11/1/2015?
The result should show category "a".
I suspect is a nested select statement, but haven't quite figured it out.
select * from yourTable where value = 0 and date > '2015-10-01' and cat in (
select distinct cat where value > 0 and date < '2015-11-1'
)
Explanation: you can split the query to two parts - the inner query with the in statement is in charge of getting the cat ID's that were positive before 11/1/15, and the where value = 0 and date > '2015-10-01' will give you those that are 0 after 10/1/15
Building off what Nir Levy said, if you only want the category to be returned, you can select just the distinct values for cat:
SELECT DISTINCT cat
FROM stack_test.your_table
WHERE value = 0 AND date > '2015-10-01' AND
cat IN (SELECT cat FROM stack_test.mysql_rows WHERE value > 0 AND date < '2015-11-01');

rows to column and vice-versa conversion

By running the following query I will get the result below. I want to convert this result into rows and whatever is there as row over ther those I want to convert into columns.
I want that project_level and account_level to come in rows and the remaining in rows goes to column.
SELECT S.Description,T.Project_Level,S.Account_Level FROM
(
SELECT DISTINCT SM.Description, COUNT(VA.FK_Status_ID) AS Project_Level,SM.Seq
From DeliveryPlatform_APPS.VA.DP_VA_Item_Status_Master SM
LEFT OUTER JOIN DeliveryPlatform_APPS.VA.DP_VA_Items VA ON SM.Status_ID = VA.FK_Status_ID
AND VA.FK_DP_Entity_Type_ID = 1 AND FK_DP_Entities=671
GROUP BY SM.Description,SM.Seq,FK_Status_ID
) AS T
INNER JOIN
(
SELECT DISTINCT SM.Description, COUNT(VA.FK_Status_ID) AS Account_Level,SM.Seq
From DeliveryPlatform_APPS.VA.DP_VA_Item_Status_Master SM
LEFT OUTER JOIN DeliveryPlatform_APPS.VA.DP_VA_Items VA ON SM.Status_ID = VA.FK_Status_ID
AND VA.FK_DP_Entity_Type_ID = 1 AND FK_DP_Entities=671
GROUP BY SM.Description,SM.Seq,FK_Status_ID
) AS S
ON S.Description=T.Description
--ORDER BY Seq
Description Project_Level Account_Level
Accepted 0 0
Closed 0 0
Delivered 0 0
Dropped 0 0
Open 0 0
Parked 0 0
Shortlisted 0 0
Work In Progress 0 0
You should check SQL Server PIVOT and UNPIVOT techniques to accomplish your intent:
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Count numbers without Zero

I have a query who works correctly, but where I want to make a change.
The query counts all notes for id_service = 89.
The notes are numbers between 0 and 3 not more.
My query counts all those notes to get an Average.
All works fine.
The change what I want is, that he counts only the notes between 1 and 3 (not 0).
I don't know how I can do that.
Example:
This query count how much time I get the number 3 with all conditions.
Here the query:
SELECT round( avg( AVG =3 ) * count( AVG ) ) AS New
, sma_famille.famille
FROM (
SELECT ROUND( SUM( note ) / count( note ) ) AS AVG
, sma_famille.famille
, sma_agents.nom
FROM sma_notes
INNER JOIN sma_famille
ON sma_famille.id_service =89
INNER JOIN sma_agents
ON sma_notes.id_agent = sma_agents.id_agent
INNER JOIN sma_service_activite
ON sma_service_activite.id_activite = sma_notes.id_activite
AND sma_service_activite.id_famille = sma_famille.id_famille
AND sma_service_activite.id_service = sma_famille.id_service
GROUP BY sma_famille.famille, sma_agents.nom
) AS FN
LEFT JOIN sma_famille
ON sma_famille.id_service =89
AND FN.famille = sma_famille.famille
GROUP BY FN.famille
An example:
In Bio I can give 2 notes per persons like Bio Part1 and Bio Part2.
In my example I have two persons.
I give in Bio Part1 the note "3" for both and in Bio Part2 i don't give a note, so that there are the note "0"!
Here the result of my query:
That is what I get:
Note Math English Bio
1 0 0 0
2 0 0 2
3 0 0 0
That is what I want:
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
Without the note "0" I must get an average of 100% for the note "3".
Now i get:
Note Math English Bio
1 0
2 0
3 2
And not
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
How can i get the "0" in the resultset
Anybody an idea?
One way to get a count of values > 0 would be:
count(case when note > 0 then note end)
(Where note is 0, the case evaluates as null which is not counted.)
Although the simplest way might be
sum(sign(note))