To calculate the difference in two different merged queries - SQL - mysql

i am having some problems creating a query of my project.
I have created a software that cyclically (every minute) interrogates 3 or 4 devices and saves the collected data (people's incoming and outgoing) on a MySQL database.
This is the structure of my table:
```
+----+----------+----------+----------+---------------------+---------+
| id | DeviceId | ValueIn | ValueOut | DateTime | Status |
+----+----------+----------+----------+---------------------+---------+
| 1 | 1 | 00000429 | 00000327 | 2020-02-20 11:59:23 | online |
| 2 | 2 | 00000350 | 00000278 | 2020-02-28 12:54:12 | online |
| 3 | 1 | 00000000 | 00000000 | 2020-01-25 11:12:13 | offline |
| 4 | 2 | 00000300 | 00000200 | 2020-01-10 08:12:54 | online |
| 5 | 2 | 00000330 | 00000250 | 2020-02-01 09:00:54 | online |
| 6 | 1 | 00000400 | 00000300 | 2020-01-24 18:05:54 | online |
| 7 | 3 | 00000600 | 00000700 | 2020-02-10 13:05:54 | online |
| 8 | 3 | 00000000 | 00000000 | 2020-01-03 15:07:21 | offline |
| 9 | 4 | 00000111 | 00000111 | 2020-02-15 17:20:01 | online |
+----+----------+----------+----------+---------------------+---------+
```
My Query should return one row for each month (grouped by month) and each device with the most recent day of the month that have been collected by the software.
The same line should show also the ValueIn and ValueOut of the previous month: I will need it to work out the difference and calculate the monthly inputs/outputs.
What the table should show:
```
+----------+-----------+----------+---------------------+----------+----------+---------------------+
| ValueIn | ValueOut | DeviceId | DateTime | OLD_IN | OLD_OUT | OLD_DateTime |
+----------+-----------+----------+---------------------+----------+----------+---------------------+
| 00000350 | 000000278 | 2 | 2020-02-28 12:54:12 | 00000300 | 00000200 | 2020-01-10 08:12:54 |
| 00000429 | 000000327 | 1 | 2020-02-20 11:59:23 | 00000400 | 00000300 | 2020-01-25 11:12:13 |
| 00000600 | 000000700 | 3 | 2020-02-10 13:05:54 | 00000000 | 00000000 | 2020-01-03 15:07:21 |
| 00000111 | 000000111 | 4 | 2020-02-15 17:20:01 | 00000000 | 00000000 | null |
+----------+-----------+----------+---------------------+----------+----------+---------------------+
```
So, 50 people entered my DeviceId 2 in February and 78 left.
In DeviceId1 incoming: 429 outgoing: 327
Is it possible ??
I did some tests: if I have no < DateTime rows the query returns no results.
This is my starting query.
SELECT
TodayAccess.ValueIn,
TodayAccess.ValueOut,
TodayAccess.DateTime,
devices.Name,
MAX( OldAccess.ValueIn ),
MAX( OldAccess.ValueOut )
FROM peoplecounter AS TodayAccess
INNER JOIN devices ON TodayAccess.DeviceId = devices.Id
RIGHT JOIN peoplecounter AS OldAccess ON TodayAccess.DeviceId = OldAccess.DeviceId
WHERE TodayAccess.Status = 'online'
AND TodayAccess.DateTime BETWEEN '2020-06-01'
AND '2020-08-31'
AND OldAccess.DateTime < '2020-06-01'
GROUP BY TodayAccess.DeviceId, MONTHNAME(TodayAccess.DateTime)
I thank all those who will help me!

Related

Update field with query using longest match number

I have the following table of call_logs:
+------------+---------------------+------------+-------------+--------------+
| id | datetime | a_number | b_number | nem |
+------------+---------------------+------------+-------------+--------------+
| 1262662410 | 2020-07-17 10:43:57 | 3415529238 | 12642356719 | |
| 1262661229 | 2020-07-17 10:43:48 | 1126751251 | 12641344559 | |
| 1262658679 | 2020-07-17 10:43:28 | 3516807236 | 16199573103 | |
+------------+---------------------+------------+-------------+--------------+
and another table of prefixes:
+---------+-------+-------------------+------+
| prefix | lenght| description | nem |
+---------+-------+-------------------+------+
| 1907 | 4 | ALASKA | ALAS |
| 1684 | 4 | AMERICAN SAMOA | ASAM |
| 1264 | 4 | ANGUILLA | AGLL |
| 1264235 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264469 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264476 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264536 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264537 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264538 | 7 | ANGUILLA - MOBILE | AGLM |
| 1264539 | 7 | ANGUILLA - MOBILE | AGLM |
+---------+-------+-------------------+------+
What MySQL query or precedure do you recommend to update the call_logs.nem field analyzing the prefixes.prefix field that best matches (with the greatest number of digits) with the field call_logs.b_number.
Example:
+------------+---------------------+------------+-------------+--------------+
| id | datetime | a_number | b_number | nem |
+------------+---------------------+------------+-------------+--------------+
| 1262662410 | 2020-07-17 10:43:57 | 3415529238 | 12642356719 | AGLM |
| 1262661229 | 2020-07-17 10:43:48 | 1126751251 | 12641344559 | AGLL |
+------------+---------------------+------------+-------------+--------------+
call_logs is a big table, it would be good to find the most efficient method.
Can anyone help me on this?
Thanks a lot!
CLARIFICATION:
Both fields: b_numberand prefix are VARCHAR type.
What type of UPDATE Query could be done in this case?
You can use not exists. Assuming that number and prefix are of numeric datatypes:
update call_logs c
inner join prefixes p on (c.b_number / p.prefix) % 10 = 0
set c.mem = p.mem
where not exists (
select 1
from prefixes p1
where (c.b_number / p1.prefix) % 10 = 0 and p1.length > p.length
)

Return multi columns result from single table with zero values

I have a single table like :
mysql> select RefID,State,StartTime,EndTime from execReports limit 5;
+--------------------------------------+-----------+---------------------+---------------------+
| RefID | State | StartTime | EndTime |
+--------------------------------------+-----------+---------------------+---------------------+
| 00019a52-8480-4431-9ad2-3767c3933627 | Completed | 2016-04-18 13:45:00 | 2016-04-18 13:45:01 |
| 00038a8a-995e-4cb2-a335-cb05d5b3e92d | Aborted | 2016-05-03 04:00:00 | 2016-05-03 04:00:02 |
| 001013f8-0b86-456f-bd59-a7ef066e565f | Completed | 2016-04-14 03:30:00 | 2016-04-14 03:30:11 |
| 001f8d23-3022-4271-bba0-200494de678a | Failed | 2016-04-30 05:00:00 | 2016-04-30 05:00:02 |
| 0027ba42-1c37-4e50-a7d6-a4e24056e080 | Completed | 2016-04-18 03:45:00 | 2016-04-18 03:45:02 |
+--------------------------------------+-----------+---------------------+---------------------+
I can extract the count of exec for each state with :
mysql> select distinct State,count(StartTime) as nbExec from execReports group by State;
+-----------+--------+
| State | nbExec |
+-----------+--------+
| Aborted | 3 |
| Completed | 14148 |
| Failed | 49 |
+-----------+--------+
4 rows in set (0.02 sec)
I can extract the count of exec for each week with :
mysql> select distinct extract(week from StartTime) as Week, count(StartTime) as nbExec from execReports group by Week;
+------+--------+
| Week | nbExec |
+------+--------+
| 14 | 1317 |
| 15 | 3051 |
| 16 | 3066 |
| 17 | 3059 |
| 18 | 3059 |
| 19 | 652 |
+------+--------+
6 rows in set (0.01 sec)
But I would like to extract a crossing table like :
+------+---------+-----------+--------+---------+---------+
| Week | nbExec | Completed | Failed | Running | Aborted |
+------+---------+-----------+--------+---------+---------+
| 14 | 1317 | 1312 | 3 | 1 | 1 |
| 15 | 3051 | 3050 | 1 | 0 | 0 |
| 16 | 3066 | 3060 | 3 | 2 | 1 |
| 17 | 3059 | 3058 | 0 | 1 | 0 |
| 18 | 3059 | 3057 | 1 | 0 | 1 |
| 19 | 652 | 652 | 0 | 0 | 0 |
+------+---------+-----------+--------+---------+---------+
I'm stuck on this for a few days. Any help appreciated.
Best regards
select extract(week from StartTime) as Week, count(StartTime) as nbExec,
sum(if(state="Completed",1,0)) Completed,
sum(if(state="Failed",1,0)) Failed,
sum(if(state="Aborted",1,0)) Aborted
from execReports group by Week;
demo
You can join multi tables for this. If you want for dynamic row to column, check this: MySQL pivot row into dynamic number of columns
SELECT
a.week,
count(a.StartTime) as nbExec,
count(b1.StartTime) as Completed,
count(b2.StartTime) as Failed,
count(b3.StartTime) as Running,
count(b4.StartTime) as Aborted,
FROM execReports a
LEFT JOIN execReports b1 ON a.refID = b1.refID and b1.state ='Completed'
LEFT JOIN execReports b2 ON a.refID = b2.refID and b2.state ='Failed'
LEFT JOIN execReports b3 ON a.refID = b3.refID and b3.state ='Running'
LEFT JOIN execReports b4 ON a.refID = b4.refID and b4.state ='Aborted'
GROUP BY 1

Conditionally move MySQL data between rows in same table

Working in Redmine, I need to copy(not move) data from certain rows to other rows based on matching project id numbers with time entries.
I have included a diagram of the table "custom_values" and my understanding of the design below(CURRENT DATA):
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | |
+----+-----------------+---------------+-----------------+-------+
At the risk of oversimplifying,
"id" = The primary key for each entry in custom_values
"customized_type" = Specifies which db table the row is referring to.
"customized_id" = Specifies the primary key for the db table entry previously specified in "customized_type".
"custom_field_id" = Specifies which custom field the row is referring to. Redmine admins can arbitrarily add and remove custom fields.
"value" = The data contained within the custom field specified by
"custom_field_id"
In my situation, the values listed in "value" are representing unique customer id numbers. The customer id numbers did not always get entered with each time entry. I need to copy the customer numbers from the project rows to the matching time entry rows. Each time entry has a project_id field.
So far, here is my mangled SQL query:
SELECT
custom_field_id,
custom_values.value AS 'CUSTOMER_NUMBER',
custom_values.customized_id AS 'PROJECT_ID_NUMBER',
custom_values.customized_type,
time_entries.comments AS 'TIME_ENTRY_COMMENTS'
FROM
redmine_tweaking.custom_values
LEFT JOIN
redmine_tweaking.time_entries ON custom_values.customized_id = time_entries.project_id
WHERE
custom_values.customized_type='Project' AND custom_values.custom_field_id=1;
The query I have so far allows me to see that I have the time entries connected properly to their matching projects, but that is all I have been able to figure out. So in other words, this SQL statement does not exactly solve my problem.
Plus, even if it did work, I think the way I laid it out looks like 200 lbs of bird poop. There must be a better/more optimized way to do this.
Any help would be greatly appreciated. I am relatively new and I have been pouring hours into solving this problem.
UPDATE:
Ok, here is the time_entries table:
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| id | project_id | user_id | issue_id | hours | comments | activity_id | spent_on | tyear | tmonth | tweek | created_on | updated_on |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| 1 | 1 | 1 | 1 | .25 | test | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 08:18:12 | 2015-11-04 10:18:12 |
| 2 | 2 | 1 | 1 | .25 | test2 | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 09:18:12 | 2015-11-04 12:18:12 |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
As opposed to the original table that I first posted, the expected output would show this:
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | 03 |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | 04 |
+----+-----------------+---------------+-----------------+-------+

How to get sum for different entry in mysql

I have table in mysql like
| service_code | charges | caller_number | duration | minutes |
+--------------+---------+---------------+----------+---------+
| 10 | 15 | 8281490235 | 00:00:00 | 1.0000 |
| 11 | 12 | 9961621709 | 00:00:00 | 0.0000 |
| 10 | 15 | 8281490235 | 01:00:44 | 60.7333 |
| 11 | 2 | 9744944316 | 01:00:44 | 60.7333 |
+--------------+---------+---------------+----------+---------+
from this table I want to get charges*minutes for each separate caller_number.
I have done like this
SELECT sum(charges*minutes) as cost from t8_m4_bill groupby caller_number
but I am not getting expected output. Please help?
SELECT caller_number,sum(charges*minutes) as cost
from t8_m4_bill
group by caller_number
order by caller_number

Order by and group by

I am trying to show delivery charges for a shop I am building, there are three tables in the database 1 for the service ie Royal Mail, Carrier..., one for the band ie. UK, Europe, Worldwide1 etc.. and one for the charges (qty = weight)
I have a database of three tables that, when joined form the following
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
| name | qty | serviceID | basis | bandID | initial_charge | chargeID | price | total_price |
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
| Collect in store | 0 | 3 | | 1 | 3 | 0.00 | 2 | 0.00 |
| Royal mail | 0 | 1 | 2 | 4 | 2.00 | 3 | 0.00 | 2.00 |
| Royal mail | 1 | 1 | 2 | 4 | 2.00 | 4 | 1.00 | 3.00 |
| APC | 0 | 2 | 1 | 1 | 0.00 | 6 | 5.95 | 5.95 |
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
Basically what I want to do is (as you can see) Royal Mail has two entries as there are more than one entry in the joined table. What I would like to do is show the highest of the two royal mail entries (I was initially trying to group by service_id) whilst also maintaining the two other services with different service id's
Any assistance would be great as this is driving me mad. I feel like I have tried every combination going!
In the example below the qty (weight) of the items is 3kg
SELECT
`service`.`name`,
`charge`.`qty`,
`service`.`serviceID`,
`band`.`bandID`,
`band`.`initial_charge`,
`charge`.`chargeID`,
`charge`.`price`,
`band`.`initial_charge` + `charge`.`price` AS `total_price`
FROM
`delivery_band` AS `band`
LEFT JOIN
`delivery_charge` AS `charge`
ON
`charge`.`bandID` = `band`.`bandID`
AND
`charge`.`qty` < '3'
LEFT JOIN
`delivery_service` AS `service`
ON
`service`.`serviceID` = `band`.`serviceID`
WHERE
FIND_IN_SET( '225', `band`.`accepted_countries` )
AND
(
`band`.`min_qty` >= '3'
OR
`band`.`min_qty` = '0'
)
AND
(
`band`.`max_qty` <= '3'
OR
`band`.`max_qty` = '0'
)
delivery_service
+-----------+------------------+
| serviceID | name |
+-----------+------------------+
| 1 | Royal mail |
| 2 | APC |
| 3 | Collect in store |
+-----------+------------------+
delivery_band
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
| bandID | serviceID | name | initial_charge | min_qty | max_qty | accepted_countries |
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
| 1 | 2 | UK Mainland | 0.00 | 0 | 0 | 225 |
| 2 | 2 | UK Offshore | 14.00 | 0 | 0 | 240 |
| 3 | 3 | Bradford Store | 0.00 | 0 | 0 | 225 |
| 4 | 1 | UK | 2.00 | 0 | 0 | 225 |
| 5 | 2 | World wide | 15.00 | 0 | 0 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20... |
| 6 | 1 | World wide Mail | 5.00 | 0 | 0 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20... |
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
delivery_charge
+----------+--------+-----+-------+
| chargeID | bandID | qty | price |
+----------+--------+-----+-------+
| 1 | 2 | 0 | 5.00 |
| 2 | 3 | 0 | 0.00 |
| 3 | 4 | 0 | 0.00 |
| 4 | 4 | 1 | 1.00 |
| 5 | 4 | 5 | 3.00 |
| 6 | 1 | 0 | 5.95 |
| 7 | 1 | 10 | 10.95 |
| 8 | 2 | 10 | 14.00 |
| 9 | 5 | 0 | 0.00 |
| 10 | 5 | 3 | 5.00 |
| 11 | 5 | 6 | 10.00 |
| 12 | 5 | 9 | 15.00 |
| 13 | 6 | 0 | 0.00 |
| 14 | 6 | 2 | 5.00 |
| 15 | 6 | 4 | 10.00 |
| 16 | 6 | 6 | 15.00 |
+----------+--------+-----+-------+
When I tried adding the charge table as a sub query and then limiting that query it gave me NULL's for all the charge table fields
If I try the following query:
SELECT
`service`.`name`,
`charge`.`qty`,
`service`.`serviceID`,
`band`.`bandID`,
`band`.`initial_charge`,
`charge`.`chargeID`,
MAX( `charge`.`price` ) AS `price`,
`band`.`initial_charge` + `charge`.`price` AS `total_price`
FROM
`delivery_band` AS `band`
LEFT JOIN
`delivery_charge` AS `charge`
ON
`charge`.`bandID` = `band`.`bandID`
AND
`charge`.`qty` < '3'
LEFT JOIN
`delivery_service` AS `service`
ON
`service`.`serviceID` = `band`.`serviceID`
WHERE
FIND_IN_SET( '225', `band`.`accepted_countries` )
AND
(
`band`.`min_qty` >= '3'
OR
`band`.`min_qty` = '0'
)
AND
(
`band`.`max_qty` <= '3'
OR
`band`.`max_qty` = '0'
)
GROUP BY
`service`.`serviceID`
I get this returned:
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
| name | qty | serviceID | bandID | initial_charge | chargeID | price | total_price |
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
| Royal mail | 0 | 1 | 4 | 2.00 | 3 | 1.00 | 2.00 |
| APC | 0 | 2 | 1 | 0.00 | 6 | 5.95 | 5.95 |
| Collect in store | 0 | 3 | 3 | 0.00 | 2 | 0.00 | 0.00 |
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
Which looks fine in principle until you realise that the chargeID = 3 has a price of 0.00 and yet the table is showing a price of 1.00 so the values seem to have become disassociated
What I would like to do is show the highest of the two royal mail entries
You can use MAX to obtain the maximum of a given column, e.g.
SELECT … MAX(charge.price) … FROM …
If you absolutely need the other columns (like charge.chargeID) to match, things will become a lot more complicated. So make sure you actually need that. For details on the general idea behind this kind of query, have a closer look at Select one value from a group based on order from other columns. Adapting this answer by #RichardTheKiwi, I came up with the following query:
SELECT s.name,
c.qty,
s.serviceID,
b.bandID,
b.initial_charge,
c.chargeID,
c.price,
b.initial_charge + c.price AS total_price
FROM delivery_band AS b,
delivery_service AS s,
(SELECT chargeID, price, qty,
#rowctr := IF(bandId = #lastBand, #rowctr+1, 1) AS rowNumber,
#lastBand := bandId AS bandId
FROM (SELECT #rowctr:=0, #lastBand:=null) init,
delivery_charge
WHERE qty < 3
ORDER BY bandId, price DESC
) AS c
WHERE FIND_IN_SET(225, b.accepted_countries)
AND (b.min_qty >= 3 OR B.min_qty = 0)
AND (b.max_qty <= 3 OR B.max_qty = 0)
AND s.serviceID = b.serviceID
AND c.bandID = b.bandID
AND c.rowNumber = 1
See this fiddle for the corresponding output. Note that I only do inner queries, not left queries, since that seems sufficient for the query in question, and keeps things a lot more readable so you can concentrate on the important parts, i.e. those involving rowNumber. The idea is that the subquery generates row numbers for the items of the same band, resetting them for the next band. When you select only rows with rowNumber being 1, you only get the highest price, with all other columns associated with that.