Implementing SUMIF() function from Excel to SQL - mysql

Lately, I have been learning how to use SQL in order to process data. Normally, I would use Python for that purpose, but SQL is required for the classes and I still very much struggle with using it comfortably in more complicated scenarios.
What I want to achieve is the same result as in the following screenshot in Excel:
Behaviour in Excel, that I want to implement in SQL
The formula I used in Excel:
=SUMIF(B$2:B2;B2;C$2:C2)
Sample of the table:
> select * from orders limit 5;
+------------+---------------+---------+
| ID | clientID | tonnage |
+------------+---------------+---------+
| 2005-01-01 | 872-13-44-365 | 10 |
| 2005-01-04 | 369-43-03-176 | 2 |
| 2005-01-05 | 408-24-90-350 | 2 |
| 2005-01-10 | 944-16-93-033 | 5 |
| 2005-01-11 | 645-32-78-780 | 14 |
+------------+---------------+---------+
The implementation is supposed to return similar results as following group by query:
select
orders.clientID as ID,
sum(orders.tonnage) as Tonnage
from orders
group by orders.clientID;
That is, return how much each client have purchased, but at the same I want it to return each step of the addition as separate record.
For an instance:
Client A bought 350 in the first order and then 231 in the second one. In such case the query would return something like this:
client A - 350 - 350 // first order
client A - 281 - 581 // second order
Example, how it would look like in Excel
I have already tried to use something like:
select
orders.clientID as ID,
sum(case when orders.clientID = <ID> then orders.tonnage end)
from orders;
But got stuck quickly, since I would need to somehow dynamically change this <ID> and store it's value in some kind of temporary variable and I can't really figure out how to implement such thing in SQL.

You can use window function for running sum.
In your case, use like this
select id, clientID, sum(tonnage) over (partition by clientID order by id) tonnageRunning
from orders
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=13a8c2d46b5ac22c5c120ac937bd6e7a

Related

Count substrings in SQL (in Digital Metaphors' ReportBuilder)

I'm trying to create a report in ReportBuilder (Digital Metaphors, not Microsoft) and I'm having trouble getting the SQL to do what I want.
I have one table with a field building:
| building |
+------------+
| WhiteHouse |
| TajMahal |
and another table with a field locations:
| id | locations |
+----+-----------------------------------------------------------------+
| 1 | WhiteHouse:RoseGarden,WhiteHouse:MapRoom,TajMahal:MainSanctuary |
| 2 | TajMahal:NorthGarden,WhiteHouse:GreenRoom |
I would like to create a table showing how many times each building is used in locations, like so:
| building | count |
+------------+-------+
| WhiteHouse | 3 |
| TajMahal | 2 |
The characters : and , are never used in building or room names. Even a quick-and-dirty solution that assumes that building names never appear in room names would be good enough for me.
Of course this would be easy to do in just about any sane programming language (total over something like /\bWhiteHouse:/); the trick will be getting RB to do it. Suggestions for workarounds are welcome.
it is possible to split locations string into pieces using the "," and ":" characters as seperators as follows in SQL Server with the help of a custom sql split function
select
p2.val,
count(p2.val)
from locations l
cross apply dbo.split(l.locations,',') p1
cross apply dbo.split(p1.val,':') p2
inner join building b
on b.building = p2.val
group by p2.val
I'm not sure there is a similar one in mysql, if so please check following solution as a template
You can try this, probably not the fastest, but certainly easier solution.
SELECT t1.building,
( SELECT SUM( ROUND( (LENGTH(t2.locations)
- LENGTH(REPLACE(t2.locations, concat(t1.building, ':'), ''))
) / (LENGTH(t1.building) + 1)
)
)
FROM table2 AS t2
) as count
FROM table1 as t1
SQL Fiddle Demo

how to sum amount based on 2 columns that are both dates in mysql

i have a tbl_remit where i need to get the last remittance.
I'm developing as system wherein I need to get the potential collection of each Employer using the Employer's last remittance x 12. Ideally, Employers should remit once every month. But there are cases where an Employer remits again for the same month for the additional employee that is newly hired. The Mysql Statement that I used was this.
SELECT Employer, MAX(AP_From) as AP_From,
MAX(AP_To) as AP_To,
MAX(Amount) as Last_Remittance,
(MAX(Amount) *12) AS LastRemit_x12
FROM view_remit
GROUP BY PEN
Result
|RemitNo.| Employer | ap_from | ap_to | amount |
| 1 | 1 |2016-01-01 |2016-01-31 | 2000 |
| 2 | 1 |2016-02-01 |2016-02-28 | 2000 |
| 3 | 1 |2016-03-01 |2016-03-31 | 2000 |
| 4 | 1 |2016-03-01 |2016-03-31 | 400 |
By doing that statement, i ended up getting the wrong potential collection.
What I've got:
400 - Last_Remittance
4800 - LastRemit_x12 (potential collection)
What I need to get:
2400 - Last_Remittance
28800 - LastRemit_x12 (potential collection)
Any help is greatly appreciated. I don't have a team in this project. this may be a novice question to some but to me it's really a complex puzzle. thank you in advance.
You want to filter the data for the last time period. So, think where rather than group by. Then, you want to aggregate by employer.
Here is one method:
SELECT Employer, MAX(AP_From) as AP_From, MAX(AP_To) as AP_To,
SUM(Amount) as Last_Remittance,
(SUM(Amount) * 12) AS LastRemit_x12
FROM view_remit vr
WHERE vr.ap_from = (SELECT MAX(vr2.ap_from)
FROM view_remit vr2
WHERE vr2.Employer = vr.Employer
)
GROUP BY Employer;
EDIT:
For performance, you want an index on view_remit(Employer, ap_from). Of course, that assumes that view_remit is really a table . . . which may be unlikely.
If you want to improve performance, you'll need to understand the view.

SUM in access query

I have a Table (T_agents) of agents each has a number of call in a field called NCH I want to create another field call NCHpercent that is the percentage of calls taken by that agent. So the formula is NCH/Total NCH.
So in the query builder I have the following and formula but it dosent work :(
NCHpercent: [NCH.T_agents] / ( SUM(SELECT [NCH.T_agents] FROM [T_agents]) )
What am I doing wrong ?
This would be easier if we could see the table structure as that impacts everything. However I hope I follow this correctly, but I imagine your table (T_agents) as something like:
+-------+-------------+------+
| ID | Agents | NCH |
+-------+-------------+------+
| 1 | agent_1 | 1 |
| 1 | agent_1 | 1 |
| 1 | agent_2 | 2 |
| 1 | agent_3 | 1 |
+-------+-------------+------+
Now assuming that is correct (and NCH is not a unique ID but a total number of calls then we can use a query like this to calculate percentage - note this is not stored in a table, this is just to display the percentage value in a query- I've also added the sum of the total in for the sake of it:
SELECT SUM([T_Agents].NCH) AS total_SUM, [T_agents].Agents, ((SUM(T_agents.NCH))/(select SUM(t_agents.NCH )from T_agents)*100) AS NCHPercent
FROM T_agents
GROUP BY [t_agents].Agents;
In my test the results would be:
2, agent_1, 40
2, agent_2, 40
1, agent_3, 20
However if I got this wrong and the NCH column is in fact
Ok. I just found the answer soing some trial an error. The answer is this code:
NCHperc: [AHT_Tenure].[Calls Handled]/(SELECT Sum(AHT_Tenure.[Calls Handled]) AS [SumaDeCalls Handled]
FROM AHT_Tenure)
By the way thank you guys. And actually the agents name dosent matter for this query since all I wanted was the percentage on each row.

Generate report from visitor counter

I have a visitor counter, the table looks like this:
id | country_code | datetime | Browser | ...
---------------------------------------------------------
1 | FR | 2014-06-20 05:00:28 | FireFox |
2 | US | 2014-06-20 05:00:28 | Chrome |
3 | ZW | 2014-06-20 05:00:28 | IE |
I want to count how many visitor I had (for example) in an hour at a certain day.
The query looks like this:
SELECT HOUR(datetime), COUNT(*) as hits
FROM counter_table WHERE datetime >= CURDATE()
GROUP BY HOUR(datetime) WITH ROLLUP
No problem with this query.
But I also want count how many visitors I got from a certain country.
I tried everything like GROUP BY HOUR(datetime), country_code WITH ROLLUP (I do not need a hourly ROLLUP for the country, I need hourly ROLLUP for hits) or JOIN queries but I can't find a good solution.
The best thing I could come up with was something like this:
SUM(IF(country_code = "AF", 1,0)) AS Afghanistan,
...
SUM(IF(country_code = "ZW", 1,0)) AS Zimbabwe
But the problem is that there are almost 400 countries in the world. I am not sure if such a long query like above would be good for performance. Unfortunately performance is very important in this case because the table is huge. But otherwise this solution provides exactly what I want.
Maybe there is another database better than MySQL for this kind of problem?

mysql query logic

I have an sql query which shows the delivery details of a vehicle. ( it uses greatest to fetch max value from a range of colums for each vehicle stop)
SELECT deliveryid AS deliverynumber, loadid1 AS loadnumberdate,
haulieraccepted AS haulier,
greatest(drop1arrivedatetime, drop2arrivedatetime, drop3arrivedatetime,
drop4arrivedatetime, drop5arrivedatetime) AS planneddate,
date(greatest(ActualDrop1Arrive, ActualDrop2Arrive, ActualDrop3Arrive,
ActualDrop4Arrive, ActualDrop5Arrive )) AS actualenddate,
mitigation
FROM deliverydetails
WHERE deliveryid=44
the output is
deliverynumber | loadnumberdate | haulier | planneddate | actualenddate | mitigation
44 | 484487 | stols transport | 2011-11-26 15:50:00 | 2011-11-26 | customerdelay
How can I add to the mysql query to compare columns 'planneddate' and 'actualenddate'? if the dates are the same then set the query field to 'ontime' else if actualenddate>planneddate then 'deliverylate'. So ideally I want the following output:
deliverynumber | loadnumberdate | haulier | planneddate | actualenddate | mitigation | Status
44 | 484487 | stols transport | 2011-11-26 15:50:00 | 2011-11-26 | customerdelay | ontime.
Thanks for the assistance.
You can use a CASE statement or IF function. Perhaps something like:
SELECT ...., IF(actualenddate>planneddate,'deliverylate','ontime') AS status FROM ....
use mysql if condition and date conversion function to check and display according to....
You can wrap your original query as a subquery. This will rename the columns. Then, use a case ... then clause to add the column.
Assuming your original query works just fine, it would look like this:
select
*,
case when (... some comparison on 'planneddate' and 'actualenddate' ...)
then <true output>
else <false output> end
from
(<your original query>) as myalias;
The trick is that the columns from the subquery are renamed, allowing you to use their new names (planneddate and actualenddate).