mysql inner join two tables with month matching - mysql

I have a two tables in MySQL and both tables in same database.
table name data
| service | count | date |
--------------------------------------
| bugss | 375 | 2022-01-01 05:00:00.00000
| fromsite | 5 | 2022-02-01 05:00:00.00000
| kbocetra | 100 | 2022-01-05 07:00:00.00000
tried for data table
SELECT SUM(`count`) AS Alertcount,
DATE_FORMAT(`date`, '%M') AS Month,
FROM data
GROUP BY DATE_FORMAT(`date`, '%Y-%m')
output:
January | 475
February | 5
another table name pd
| group | minutes | projdate |
--------------------------------
gcp | 145 | 2022-01-01 05:00:00.00000
azure | 10 | 2022-02-01 05:00:00.00000
aws | 80 | 2022-01-05 07:00:00.00000
i tried below command for separate tables, for pd table as below ..which gives output as
SELECT SUM(`minutes`) AS Hours,
DATE_FORMAT(`group `, '%M') AS Month
FROM pd
GROUP BY DATE_FORMAT(`group`, '%Y-%m')
output:
January | 225
February | 10
and im expected the ouput like below, and total count would be as output of two tables count/minutes i.e., 475/225
and 5/10.
please help, i red about inner statement, but didn't worked.
Month
total
January
0.78
February
2

Run the following command and see the results.
SELECT
a.`Month`,
a.`Hours` / b.`Alertcount` as 'total'
FROM
(
SELECT
SUM( `minutes` ) AS Hours,
DATE_FORMAT( `group `, '%M' ) AS 'Month'
FROM
pd
GROUP BY
DATE_FORMAT( `group`, '%Y-%m' )
) a
INNER JOIN (
SELECT
SUM( `count` ) AS Alertcount,
DATE_FORMAT( `date`, '%M' ) AS 'Month'
FROM
DATA
GROUP BY
DATE_FORMAT( `date`, '%Y-%m' )
) b ON a.`Month` = b.`Month`

When selecting the tables you can use the division operator as you can see here.

Related

Earliest time of daily maximum values

I have a table that logs weather data variables by datetime like this:
|------------------|------------| ----
| LogDateTime | Temp | ...
+------------------|------------| ----
| 2020-01-01 00:00 | 20.1 | ...
| 2020-01-01 00:05 | 20.1 | ...
| 2020-01-01 00:10 | 19.9 | ...
| 2020-01-01 00:15 | 19.8 | ...
---------------------------------------
From that table I want to return the earliest time of the maximum temperature for each day like this (just the time portion of the datetime value):
|------------|----------------------
| LogDate | LogTime| MaxTemp
+---------------------|--------------
| 2020-01-01 | 14:00 | 24.5
| 2020-01-02 | 15:12 | 23.2
| 2020-01-03 | 10:12 | 25.1
| 2020-01-04 | 12:14 | 28.8
--------------------------------
The query I have to return this so far is the below, but it returns the earliest temperature for each day instead of the earliest occurrence of the maximum temperature for each day
SELECT TIME(a.LogDateTime), a.Temp
FROM Monthly a
INNER JOIN (
SELECT TIME(LogDateTime), LogDateTime, MAX(Temp) Temp
FROM Monthly
GROUP BY LogDateTime
) b ON a.LogDateTime = b.LogDateTime AND a.Temp= b.Temp
GROUP BY DATE(a.LogDateTime)
I then want to use that query to update a table of one row per day that summarises the minimum and maximum values with a query something like this but update the time rather than the actual maximum temperature:
UPDATE Dayfile AS d
JOIN (
SELECT DATE(LogDateTime) AS date, MAX(Temp) AS Temps
FROM Monthly
GROUP BY date
) AS m ON DATE(d.LogDate) = m.date
SET d.MaxTemp = m.Temps
Your version of MariaDB supports window functions, so use ROW_NUMBER():
select LogDateTime, Temp
from (
select *,
row_number() over (partition by date(LogDateTime) order by Temp desc, LogDateTime) rn
from Monthly
) t
where t.rn = 1
See a simplified demo.
Use it to update Dayfile like this:
update Dayfile d
inner join (
select LogDateTime, Temp
from (
select *,
row_number() over (partition by date(LogDateTime) order by Temp desc, LogDateTime) rn
from Monthly
) t
where t.rn = 1
) m on date(d.LogDate) = m.date
set d.MaxTemp = m.Temp

Can't run MySQL code that seem to work for others

The goal of the code is to select Month, SaleID, Total and Growth. I can display Month, SaleID and Total but can't get Growth to work because it calculates from the the first row always. What am I doing wrong?
I've tried setting up variables, Emulating LAG(), PREV, CURRENT, NEXT to get the row the calculation should use but it won't register the native functions.
CREATE VIEW SalesTemp
AS
SELECT
DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS Month,
Sales.SaleID,
Sales.Total
FROM Sales
WHERE SaleDate BETWEEN '2018-04-00' AND '2040-00-00'
GROUP BY DATE_FORMAT(Sales.SaleDate, "%Y-%m");
SELECT * FROM SalesTemp;
DROP VIEW IF EXISTS PercentageGrowth;
CREATE VIEW PercentageGrowth
AS
SELECT
DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS Month,
Sales.SaleID,
Sales.Total,
CONCAT(ROUND(((Sales.Total) - SalesTemp.Total) / (SELECT SalesTemp.Total FROM SalesTemp GROUP BY DATE_FORMAT(SalesTemp.Month, "%Y-%m")) * 100, 2), "%") AS Growth
FROM Sales, SalesTemp
GROUP BY DATE_FORMAT(Sales.SaleDate, "%Y-%m");
SELECT * FROM PercentageGrowth;
DROP VIEW PercentageGrowth;
DROP VIEW SalesTemp;
I want it to display growth of a company through the calculation of ((newValue - oldValue) / oldValue).
Since I can't link pictures I'll ascii what the result is. What I get from the SELECT now is:
+--------------------------------------+
| Month | SaleID | Total | Growth |
| ------- | ------ | ------- | ------- |
| 2018-04 | 1 | 310.46 | 00.00% |
| 2018-05 | 3 | 2160.62 | 595.54% |
| 2018-06 | 6 | 1087.89 | 250.21% |
| 2018-07 | 14 | 2314.54 | 645.09% |
+--------------------------------------+
I want it to say:
+--------------------------------------+
| Month | SaleID | Total | Growth |
| ------- | ------ | ------- | ------- |
| 2018-04 | 1 | 310.46 | 00.00% |
| 2018-05 | 3 | 2160.62 | 595.54% |
| 2018-06 | 6 | 1087.89 | -49.64% |
| 2018-07 | 14 | 2314.54 | 112.76% |
+--------------------------------------+
Currently, you are cross joining your table pairings across all SaleID and all formatted date months and this is then further impacted by your unclear aggregations.
Assuming you use MySQL 8+, consider a couple of CTEs which includes LAG by one offset of your aggregated month totals:
WITH cte1 AS
(SELECT DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS `Month`,
Sales.SaleID,
SUM(Sales.Total) AS `Total_Sales`
FROM Sales
WHERE SaleDate BETWEEN '2018-04-00' AND '2040-00-00'
GROUP BY
DATE_FORMAT(Sales.SaleDate, "%Y-%m"),
Sales.SaleID
),
cte2 AS
(SELECT *,
LAG(`Total_Sales`) OVER (PARTITION BY `SaleID`
ORDER BY `Month`) AS `Lag_Total_Sales`
FROM cte1)
SELECT `Month`, `SaleID`, `Total_Sales`,
CONCAT(
ROUND(
(`Total_Sales` - `Lag_Total_Sales`) / `Lag_Total_Sales`
, 2) * 100
, '%') AS `Growth`
FROM cte2
For MySQL 5.7 or less, consider a self-join of subquery that explicitly joins SaleID and any date in last month normalizing all dates to the first of their respective months.
SELECT DATE_FORMAT(curr.FirstMonth, "%Y-%m") AS `Month`,
curr.SaleID,
curr.Total_Sales,
CONCAT(
ROUND((`curr`.Total_Sales - `prev`.Total_Sales) / `prev`.Total_Sales
, 2)*100
, '%') AS `Growth`
FROM
(SELECT DATE_ADD(LAST_DAY(DATE_SUB(SaleDate, INTERVAL 1 MONTH))
, INTERVAL 1 DAY) As FirstMonth,
SaleID,
SUM(`Total`) As `Total_Sales`
FROM Sales
GROUP BY
DATE_ADD(LAST_DAY(DATE_SUB(SaleDate, INTERVAL 1 MONTH))
, INTERVAL 1 DAY),
SaleID
) AS `curr`
LEFT JOIN
(SELECT DATE_ADD(LAST_DAY(DATE_SUB(SaleDate, INTERVAL 1 MONTH))
, INTERVAL 1 DAY) As FirstMonth,
SaleID,
SUM(`Total`) As `Total_Sales`
FROM Sales
GROUP BY
DATE_ADD(LAST_DAY(DATE_SUB(SaleDate,
INTERVAL 1 MONTH))
, INTERVAL 1 DAY),
SaleID
) AS `prev`
ON `curr`.SaleID = `prev`.SaleID
AND `curr`.FirstMonth - INTERVAL 1 MONTH = `prev`.FirstMonth
AND `curr`.FirstMonth BETWEEN '2018-04-00' AND '2040-00-00'
Rextester Demo (MySQL 5.7 version)

Get the count() where created_date is cumulative and date based

I'm aware that there are several answers on SO about cumulative totals. I have experimented and have not found a solution to my problem.
Here is a sqlfiddle.
We have a contacts table with two fields, eid and create_time:
eid create_time
991772 April, 21 2016 11:34:21
989628 April, 17 2016 02:19:57
985557 April, 04 2016 09:56:39
981920 March, 30 2016 11:03:12
981111 March, 30 2016 09:36:48
I would like to select the number of new contacts in each month along with the size of our contacts database at the end of each month. New contacts by year and month is simple enough. For the size of the contacts table at the end of each month I did some research and found what looked to be a straight forwards method:
set #csum = 0;
select
year(c.create_time) as yr,
month(c.create_time) as mth,
count(c.eid) as new_contacts,
(#csum + count(c.eid)) as cumulative_contacts
from
contacts c
group by
yr,
mth
That runs but gives me unexpected results.
If I run:
select count(*) from contacts where date(create_time) < current_date
I get the total number of records in the table 146.
I therefore expected the final row in my query using #csum to have 146 for April 2016. It has only 3?
What my goal is for field cumulative_contacts:
For the record with e.g. January 2016.
select count(*) from contacts where date(create_time) < '2016-02-01';
And the record for February would have:
select count(*) from contacts where date(create_time) < '2016-03-01';
And so on
Try this, a bit of modification from your sql;)
CREATE TABLE IF NOT EXISTS `contacts` (
`eid` char(50) DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
INSERT INTO `contacts` (`eid`, `create_time`) VALUES
('991772', '2016-04-21 11:34:21'),
('989628', '2016-04-17 02:19:57'),
('985557', '2016-04-04 09:56:39'),
('981920', '2016-03-30 11:03:12'),
('981111', '2016-03-30 09:36:48');
SET #csum = 0;
SELECT t.*, #csum:=(#csum + new_contacts) AS cumulative_contacts
FROM (
SELECT YEAR(c.create_time) AS yr, MONTH(c.create_time) AS mth, COUNT(c.eid) AS new_contacts
FROM contacts c
GROUP BY yr, mth) t
Output results is
| yr | mth | new_contacts | cumulative_contacts |
------ ----- -------------- ---------------------
| 2016 | 3 | 2 | 2 |
| 2016 | 4 | 3 | 5 |
This sql will get the cumulative sum and is pretty efficient. It numbers each row first and then uses that as the cumulative sum.
SELECT s1.yr, s1.mth, s1.new_contacts, s2.cummulative_contacts
FROM
(SELECT
YEAR(create_time) AS yr,
MONTH(create_time) AS mth,
COUNT(eid) AS new_contacts,
MAX(eid) AS max_eid
FROM
contacts
GROUP BY
yr,
mth
ORDER BY create_time) s1 INNER JOIN
(SELECT eid, (#sum:=#sum+1) AS cummulative_contacts
FROM
contacts INNER JOIN
(SELECT #sum := 0) r
ORDER BY create_time) s2 ON max_eid=s2.eid;
--Result sample--
| yr | mth | new_contacts | cumulative_contacts |
|------|-----|--------------|---------------------|
| 2016 | 1 | 4 | 132 |
| 2016 | 2 | 4 | 136 |
| 2016 | 3 | 7 | 143 |
| 2016 | 4 | 3 | 146 |
Try this: fiddele
Here you have a "greater than or equal" join, so each group "contains" all previous values. Times 12 part, converts the hole comparation to months. I did offer this solution as it is not MySql dependant. (can be implemented on many other DBs with minimun or no changes)
select dates.yr, dates.mth, dates.new_contacts, sum(NC.new_contacts) as cumulative_new_contacts
from (
select
year(c.create_time) as yr,
month(c.create_time) as mth,
count(c.eid) as new_contacts
from
contacts c
group by
year(c.create_time),
month(c.create_time)
) as dates
left join
(
select
year(c.create_time) as yr,
month(c.create_time) as mth,
count(c.eid) as new_contacts
from
contacts c
group by
year(c.create_time),
month(c.create_time)
) as NC
on dates.yr*12+dates.mth >= NC.yr*12+NC.mth
group by
dates.yr,
dates.mth,
dates.new_contacts -- not needed by MySql, present here for other DBs compatibility
order by 1,2

MySQL UNION does not seem to work correctly

I have an SQL query I am using to pull data from an orders database. I am querying 2 tables and combining the results using UNION ALL. However, the UNION ALL does not seem to work as expected. Here is the query I am using:
SELECT year(oc_order.date_added) AS year, COUNT(oc_order.order_id) as cnt, SUM( ifnull(oc_order.new_total,oc_order.total) ) as total
FROM oc_order
WHERE oc_order.order_status_id IN (1,3,5)
AND MONTH(oc_order.date_added) BETWEEN '01' AND '02'
AND DAY(oc_order.date_added) BETWEEN '01' AND '31'
GROUP BY year(oc_order.date_added)
UNION ALL
SELECT ifnull(year(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),year(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) AS year, COUNT(oc_return_custom.return_id) as cnt, SUM( oc_return_custom.total ) as total
FROM oc_return_custom
WHERE ifnull(MONTH(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),MONTH(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) BETWEEN '01' AND '02'
AND ifnull(DAY(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),DAY(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) BETWEEN '01' AND '31'
GROUP BY ifnull(year(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),year(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) )
ORDER BY year DESC
This is what I get from the query:
+=======+========+=======+
| year | cnt | total |
+=======+========+=======+
| 2016 | 200 | 1000 |
| 2016 | 50 | 200 |
| 2015 | 100 | 800 |
| 2015 | 10 | 50 |
+=======+========+=======+
But this is what I wanted to get:
+=======+========+=======+
| year | cnt | total |
+=======+========+=======+
| 2016 | 250 | 1200 |
| 2015 | 110 | 850 |
+=======+========+=======+
Can someone tell me what I am doing wrong???
Notes:
The oc_order table's date_added column is datetime whereas oc_return_custom 's date_added column is just text.
UNION ALL simply puts together two data sets produced by separate GROUP BY operations.
To get the expected result set you have to wrap the query in a subquery and apply an additional GROUP BY:
SELECT year, SUM(cnt) AS cnt, SUM(total) AS total
FROM ( ... your query here ...) AS t
GROUP BY year

Getting grouped data in column format rather than by rows from a MySQL query

It's difficult to make a more descriptive title for this. I've spent a couple of hours on it now and figured best to just ask someone!
I have 3 tables, which look something like this
Locations:
id | name
1 | Plymouth
2 | Torquay
etc
salesRecords
itemId | saleDate (unix) | qty | saleLocation |
123 | UNIXTIMESTAMP | 1 | 1 |
458 | UNIXTIMESTAMP | 4 | 2 |
215 | UNIXTIMESTAMP | 2 | 2 |
541 | UNIXTIMESTAMP | 1 | 1 |
products
itemId | brand |
123 | 1 | etc
Obviously each table actually contains more data than that, but that's all that is required for this.
What I need is an output which can tell me how many of each item, or group of items a particular location has sold. I need something that looks like this:
OUTPUT
mnth | yr | location1Sales | location2Sales
10 | 2006 | 14 | 7
11 | 2006 | 13 | 12
12 | 2006 | 8 | 1 ... etc
The question that I posted here yesterday:
MySQL subquery to get a list of IDs from one table to query a second table
Was kindly answered and gave me a fairly efficient query to retrieve a TOTAL of combined sales across all locations (there's no location data related to the query posted in yesterday's question at all) and that works fine.
I can of course add an additional clause to the WHERE and get a single location's data only, but I want all of them in one table.
Today am trying to add in some grouping/where clause to add the locations into the query but can't get my head around how to create a table of the format above.
I've got as far as getting location data output in rows, but I need it in column form as per the above
SELECT DAY( FROM_UNIXTIME( saleDate ) ) AS
DAY , MONTH( FROM_UNIXTIME( saleDate ) ) AS mnth,
YEAR( FROM_UNIXTIME( saleDate ) ) AS yr,
COUNT( s.id ) AS invCount, locations.name,
s.location, SUM( quantity ) AS saleQty
FROM salesRecords s
LEFT JOIN locations ON locations.id = s.location
INNER JOIN products ON s.itemNo = products.id
WHERE products.brand = '313' <=== X
AND s.location LIKE '1\_%' <=== XX
GROUP BY `locations`.`name` , mnth, yr
X: brand ID is a variable required to group products by brand
XX: locations are queried like this as the saleLocation is held in the dB in the form "locationId_tilId" as sales are tracked by til, although for the purpose of this data, I need all by location ID only, I'm not concerned with the tilId
I am trying to get this data and turn it into a Javascript array so I can use the brilliant amCharts plugin to chart it.
select
year( FROM_UNIXTIME( saleDate )) as yr,
month( FROM_UNIXTIME( saleDate )) as mnth,
sum( if( saleLocation = 1, qty, 0 )) as Location1Sales,
sum( if( saleLocation = 2, qty, 0 )) as Location1Sales
from
salesRecords
group by
year( FROM_UNIXTIME( saleDate )),
month( FROM_UNIXTIME( saleDate ))
If you have more sales locations, just add them to the SUM( IF() ) constructs...