MySQL find Count grouped by per month - mysql

For my problem the general structure of the tables is:
the Workers are located in different Branches (Branch table).
Prospective customer register (Registration table) as a Customer (Customer table)
and can order the products to buy (Order table).
Branch Table:
+------------+--------------+-----------------+
| 'branchId' | 'street' | 'city' |
+------------+--------------+-----------------+
| 'B002' | 'Clover Dr' | 'London' |
| 'B003' | 'Main St' | 'Glagsow' |
| 'B004' | 'Manse Rd' | 'Bristol' |
| 'B005' | 'Deer Rd' | 'London' |
| 'B007' | 'Argyll St' | 'Los Angeles' |
| 'B008' | 'Mission St' | 'San Francisco' |
| 'B009' | 'SOMA' | 'San Francisco' |
+------------+--------------+-----------------+
Customer Table:
+--------------+----------+-----------+-----------------+
| 'customerId' | 'fName' | 'lName' | 'telNo' |
+--------------+----------+-----------+-----------------+
| 'CR56' | 'Aline' | 'Stewart' | '0141-848-1825' |
| 'CR58' | 'Jacky' | 'Ho' | '0123-1325434' |
| 'CR62' | 'Mary' | 'Tregar' | '01224-196720' |
| 'CR74' | 'Mike' | 'Ritchie' | '01475-392178' |
| 'CR76' | 'John' | 'Kay' | '0207-774-5632' |
+--------------+----------+-----------+-----------------+
Registration Table:
+--------------+------------+------------+-----------------------+
| 'customerId' | 'branchId' | 'workerId' | 'joiningDate' |
+--------------+------------+------------+-----------------------+
| 'CR56' | 'B003' | 'SG37' | '2004-05-02 12:00:00' |
| 'CR58' | 'B003' | 'SA9' | '2004-05-03 12:00:00' |
| 'CR62' | 'B007' | 'SA9' | '2004-05-01 12:00:00' |
| 'CR74' | 'B004' | 'SG37' | '2004-04-04 12:00:00' |
| 'CR76' | 'B005' | 'SL41' | '2004-03-03 12:00:00' |
+--------------+------------+------------+-----------------------+
Order Table:
+--------------+---------------+-----------------------+
| 'customerId' | 'productId' | 'orderDate' |
+--------------+---------------+-----------------------+
| 'CR56' | 'PA14' | '2004-05-04 11:30:00' |
| 'CR62' | 'PA14' | '2004-05-04 14:00:00' |
| 'CR56' | 'PG36' | '2004-06-07 11:00:00' |
| 'CR56' | 'PG4' | '2004-04-14 12:05:00' |
| 'CR76' | 'PG4' | '2004-04-04 10:15:00' |
+--------------+---------------+-----------------------+
I am trying to form a query to find the number of orders per Branch within 1, 2, and 3 months of client Registration.
Let's say for example
+----------+------------+-----------------+
| 'months' | 'branchId' | 'numberOfOrder' |
+----------+------------+-----------------+
| 1 | 'B003' | 2 |
| 2 | 'B004' | 1 |
+----------+------------+-----------------+
I tried to group the table by month and date but I am stuck and not able to proceed forward.
Does anyone has any ideas and help me unblock?
I started doing something like this, but I am completely lost at the moment.
SELECT
COUNT(DISTINCT o.orderDate) AS 'count'
FROM
Order o, Registration r
WHERE
o.orderDate BETWEEN DATE('2001-01-01') AND DATE('2005-01-31')
GROUP BY YEAR(o.orderDate), MONTH(o.orderDate);
But this seems I am pretty far from what I am trying to achieve.

I'm not entirely sure of what your desired result is, but with this query you can get the count of orders, per branch, within 3 months after registration.
SELECT
reg.branchId,
COUNT(reg.branchId) AS 'orderCount'
FROM `order` AS ord INNER JOIN `registration` AS reg
ON ord.customerId = reg.customerId
WHERE reg.joiningDate BETWEEN reg.joiningDate AND DATE_ADD(reg.joiningDate, INTERVAL 3 MONTH)
GROUP BY reg.branchId
Result
Is this what you wanted to do?

Your desired result has nothing in common with your data.
So i assume you want the Order count for every branch.
I added the year also, because it is usually needed and doesn't bother if your data don't go over one year
Update:
Now ot only select orders which was place in the 3 month since the a customer joined
.It is limited by the date_add in the where clause
CREATE TABLE registration
(`customerId` varchar(4), `branchId` varchar(4), `workerId` varchar(4), `joiningDate` datetime)
;
INSERT INTO registration
(`customerId`, `branchId`, `workerId`, `joiningDate`)
VALUES
('CR56', 'B003', 'SG37', '2004-05-02 12:00:00'),
('CR58', 'B003', 'SA9', '2004-05-03 12:00:00'),
('CR62', 'B007', 'SA9', '2004-05-01 12:00:00'),
('CR74', 'B004', 'SG37', '2004-04-04 12:00:00'),
('CR76', 'B005', 'SL41', '2004-03-03 12:00:00')
;
✓
✓
CREATE TABLE `order`
(`customerId` varchar(4), `productId` varchar(4), `orderDate` datetime)
;
INSERT INTO `order`
(`customerId`, `productId`, `orderDate`)
VALUES
('CR56', 'PA14', '2004-05-04 11:30:00'),
('CR62', 'PA14', '2004-05-04 14:00:00'),
('CR56', 'PG36', '2004-06-07 11:00:00'),
('CR56', 'PG4', '2004-04-14 12:05:00'),
('CR76', 'PG4', '2004-04-04 10:15:00')
;
✓
✓
SELECT MONTH(o.`orderDate`),r.branchId, COUNT(*) numberOfOrder
FROM registration r inner join `order` o ON r.`customerId` = o.`customerId`
WHERE o.`orderDate` BETWEEN r.`joiningDate` AND DATE_ADD(r.`joiningDate`, INTERVAL 3 MONTH)
GROUP BY YEAR(o.`orderDate`),MONTH(o.`orderDate`),r.branchId
MONTH(o.`orderDate`) | branchId | numberOfOrder
-------------------: | :------- | ------------:
4 | B005 | 1
5 | B003 | 1
5 | B007 | 1
6 | B003 | 1
db<>fiddle here

You may try below query i guess, having moths calculated on the basis of differences in orderDate and joiningDate -
SELECT abs(ceil(datediff(o.`orderDate`, r.`joiningDate`)/30)) months_join,r.branchId, COUNT(*) numberOfOrder
FROM registration r inner join `order` o ON r.`customerId` = o.`customerId`
GROUP BY YEAR(o.`orderDate`), abs(ceil(datediff(o.`orderDate`, r.`joiningDate`)/30)),r.branchId

Related

Combine Two Queries with Separate Indexes

I have two queries that pull data from two different tables, but I need them to pull in the same report. I have a shared key between them, and the first table has one entry that corresponds to many entries in the second table.
My first query:
SELECT Proposal_ID,
substr(Proposal_Name, 1, 3) AS Prefix,
substr(Proposal_Name, 4, 6) AS `Number`,
Institution,
CollegeCode,
DepartmentCode,
Proposer_FirstName,
Proposer_LastName
FROM proposals.proposal
WHERE Institution = 'T';
Sample Data:
+----+--------+--------+-------+----------+----------+-----------+----------+
| ID | Prefix | Number | Inst. | CollCode | DeptCode | FirstName | LastName |
+----+--------+--------+-------+----------+----------+-----------+----------+
| 18 | SYP | 4675 | T | AS | SOC | Linda | McGaff |
+----+--------+--------+-------+----------+----------+-----------+----------+
| 20 | GEO | 4340 | T | AS | SGS | Teddy | Graham |
+----+--------+--------+-------+----------+----------+-----------+----------+
My second query:
SELECT Parent_Proposal,
SUBSTRING_INDEX(GROUP_CONCAT(`status`.`Status_Code` ORDER BY `status`.`Status_Time` DESC), ',', 1) AS status_code,
SUBSTRING_INDEX(GROUP_CONCAT(`status`.`Status_Time` ORDER BY `status`.`Status_Time` DESC), ',', 1) AS status_timestamp
FROM proposals.`status`
GROUP BY `status`.Parent_Proposal
Sample Data:
+-----------------+-------------+----------------------+
| Parent_Proposal | Status_Code | Status_Time |
+-----------------+-------------+----------------------+
| 18 | 40 | 2016-11-09 06:30:35 |
+-----------------+-------------+----------------------+
| 20 | 11 | 2017-03-20 10:26:31 |
+-----------------+-------------+----------------------+
I basically need to pull the most recent Status_Code and Status_Timestamp based on the Status_Timestamp and then relate that to the first table with the Parent_Proposal column.
Is there a way to group a subset of results without grouping all of the data together?
Expected Result:
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| ID | Prefix | Number | Inst. | CollCode | DeptCode | FName | LName | Status_Code | Status_Time |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| 18 | SYP | 4675 | T | AS | SOC | Linda | McGaff | 40 | 2016-11-09 06:30:35 |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| 20 | 11 | GEO | 4340 | AS | SGS | Teddy | Graham | 11 | 2017-03-20 10:26:31 |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
Thanks for any help and insight!
I think you want this. Just join your two tables together, and then do an additional join to a subquery on the status table to find the latest record for each parent proposal.
SELECT
p.Proposal_ID,
SUBSTR(p.Proposal_Name, 1, 3) AS Prefix,
SUBSTR(p.Proposal_Name, 4, 6) AS Number,
p.Institution,
p.CollegeCode,
p.DepartmentCode,
p.Proposer_FirstName,
p.Proposer_LastName,
s1.Status_Code,
s1.Status_Time
FROM proposals.proposal p
LEFT JOIN proposals.status s1
ON p.ID = s1.Parent_Proposal
INNER JOIN
(
SELECT Parent_Proposal, MAX(Status_Time) AS Max_Status_Time
FROM proposals.status
GROUP BY Parent_Proposal
) s2
ON s1.Parent_Proposal = s2.Parent_Proposal AND s1.Status_Time = s2.Max_Status_Time
WHERE
p.Institution = 'T';

SQL select query with multiple conditions issue

I have a problem with a SQL select query, I can't figure out what it needs to be.
This is what my items table look like:
| id | i_id | last_seen | spot |
----------------------------------------------------
| 1 | ls100 | 2017-03-10 15:30:40 | spot800 |
| 2 | ls100 | 2017-03-10 16:20:15 | spot753 |
| 3 | ls200 | 2017-03-10 16:33:10 | spot800 |
| 4 | ls300 | 2017-03-10 15:30:40 | spot800 |
| 5 | ls300 | 2017-03-10 12:10:30 | spot800 |
| 6 | ls400 | 2017-03-10 10:30:10 | spot800 |
This is what I'm trying to obtain:
| id | i_id | last_seen | spot |
----------------------------------------------------
| 3 | ls200 | 2017-03-10 16:33:10 | spot800 |
| 5 | ls300 | 2017-03-10 12:10:30 | spot800 |
So I need to have the rows where spot= 'spot800', last_seen = MAX(but only if the DateTime is the newest compared to all spots with the samei_id`), and at last the DateTime must be bigger than '2017-03-10 11:00:00'.
This is what I have so far:
SELECT *
FROM items
WHERE spot = 'spot800'
HAVING MAX(`last_seen`)
AND `last_seen` > '2017-03-10 11:00:00'
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,i_id INT NOT NULL
,last_seen DATETIME NOT NULL
,spot INT NOT NULL
);
INSERT INTO my_table VALUES
(1,100,'2017-03-10 15:30:40',800),
(2,100,'2017-03-10 14:20:15',753),
(3,200,'2017-03-10 16:33:10',800),
(4,300,'2017-03-10 15:30:40',800),
(5,300,'2017-03-10 12:10:30',800),
(6,400,'2017-03-10 10:30:10',800);
SELECT [DISTINCT] x.*
FROM my_table x
LEFT
JOIN my_table y
ON y.i_id = x.i_id
AND y.last_seen < x.last_seen
WHERE x.last_seen > '2017-03-10 11:00:00'
AND x.spot = 800
AND y.id IS NULL;
----+------+---------------------+------+
| id | i_id | last_seen | spot |
+----+------+---------------------+------+
| 3 | 200 | 2017-03-10 16:33:10 | 800 |
| 5 | 300 | 2017-03-10 12:10:30 | 800 |
+----+------+---------------------+------+
2 rows in set (0.00 sec)
Use MAX and GROUP BY.
SELECT id, i_id, MAX(last_seen), spot
FROM items
WHERE spot = 'spot800'
AND last_seen > '2017-03-10 11:00:00'
GROUP BY id, i_id, spot
There is several things wrng with your statement.
Firstly, HAVING must be accompanied with a GROUP BY clause, so it's not what you are looking for.
Also, MAX is an aggregate, not a boolean, function. That is, it cannot be used in filters, such as a where clause or a having clause. Also, if it did work, MAX would only return the entry that contains the time as '2017-03-10 16:33:10'. Not what you expected.
Try this instead:
SELECT * FROM items WHERE (spot='spot800' AND last_seen > '2017-03-10 11:00:00');

MySql select products with multiple sub-products from 2 tables

I am trying to select products from an order in my webshop MySQL database.
This is the structure of the 2 tables I am trying to get information from
Table order_product:
| order_product_id | order_id | product_id | name___ | model | quantity | price_ | total_ |
| 63072_________| 41761__ | 4022____ | Product 1 | P001_| 2______| 23.95 | 47.90 |
Table of order_product_option:
| order_id | order_product_id | product_product_id | name__ | price |
| 41761__| 63072_________| 4022____________| option 1 | 0.90_|
| 41761__| 63072_________| 4022____________| option 2 | 3.25_|
| 41761__| 63072_________| 4022____________| option 3 | 5.90_|
I need to get the order_product and all of its options in one overview with a select query but I getting stuck with multiple options for one product.
This is what I have so far
(SELECT
`order_product_id`,
`order_id`,
`product_id`,
`name`,
`model`,
`quantity`,
`price`,
`total`
FROM `order_product`
WHERE `order_id` = 41761)
UNION (SELECT
0 AS order_product_id,
T1.`order_id`,
0 AS product_id,
T1.`name`,
'option' AS model,
T2.`quantity`,
T1.`price`,
SUM(T1.`price` * T2.`quantity`) AS total
FROM `order_product_option` T1
INNER JOIN `order_product` T2 ON T1.`order_product_id` = T2.`order_product_id`
WHERE T1.`order_id` = 41761)
The outcome of this statement:
| order_product_id | order_id | product_id | name___ | model | quantity | price | total__|
| 63072_________| 41761__| 4022_____| Product 1 | P001_| 2_____ | 23.95 | 47.90 |
| 0_____________| 41761__| 0________| Option 1_ | option_| 2_____ | 0.90 | 10.05 |
But I want the outcome to be:
| order_product_id | order_id | product_id | name___ | model | quantity | price | total__|
| 63072_________| 41761__| 4022_____| Product 1 | P001__| 2_____ | 23.95 | 47.90 |
| 0_____________| 41761__| 0________| Option 1_ | option_| 2_____ | 0.90_ | 1.80_ |
| 0_____________| 41761__| 0________| Option 2_ | option_| 2_____ | 3.25_ | 6.50_ |
| 0_____________| 41761__| 0________| Option 3_ | option_| 2_____ | 5.90_ | 11.80 |
It only selects on option and the total price is incorrect.
How can I fix it so it selects all options with the right price?
Try this version:
(SELECT `order_product_id`, `order_id`, `product_id`, `name`,
`model`, `quantity`, `price`, `total`
FROM `order_product`
WHERE `order_id` = 41761
)
UNION ALL
(SELECT 0 AS order_product_id, T1.`order_id`, 0 AS product_id, T1.`name`,
'option' AS model, T2.`quantity`, T1.`price`, T1.`price` * T2.`quantity` AS total
FROM `order_product_option` T1 INNER JOIN
`order_product` T2
ON T1.`order_product_id` = T2.`order_product_id`
WHERE T1.`order_id` = 41761
)
The problem with your query is that the second subquery has a sum() function. This turns the query into an aggregation query that returns one row. You don't need the aggregation.
By the way, most databases would generate a compiler error, because you have lots of columns in the select that are not in a group by (because you had no group by). MySQL has extended SQL to allow this construct.

Update the next row of the target row in MySQL

Suppose I have a table that tracks if a payment is missed like this:
+----+---------+------------+------------+---------+--------+
| id | loan_id | amount_due | due_at | paid_at | missed |
+----+---------+------------+------------+---------+--------+
| 1 | 1 | 100 | 2013-08-17 | NULL | NULL |
| 5 | 1 | 100 | 2013-09-17 | NULL | NULL |
| 7 | 1 | 100 | 2013-10-17 | NULL | NULL |
+----+---------+------------+------------+---------+--------+
And, for example, I ran a query that checks if a payment is missed like this:
UPDATE loan_payments
SET missed = 1
WHERE DATEDIFF(NOW(), due_at) >= 10
AND paid_at IS NULL
Then suppose that the row with id = 1 gets affected. I want the amount_due of row with id = 1 be added to the amount_due of the next row so the table would look like this:
+----+---------+------------+------------+---------+--------+
| id | loan_id | amount_due | due_at | paid_at | missed |
+----+---------+------------+------------+---------+--------+
| 1 | 1 | 100 | 2013-08-17 | NULL | 1 |
| 5 | 1 | 200 | 2013-09-17 | NULL | NULL |
| 7 | 1 | 100 | 2013-10-17 | NULL | NULL |
+----+---------+------------+------------+---------+--------+
Any advice on how to do it?
Thanks
Take a look at this :
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE loan_payments
(`id` int, `loan_id` int, `amount_due` int,
`due_at` varchar(10), `paid_at` varchar(4), `missed` varchar(4))
;
INSERT INTO loan_payments
(`id`, `loan_id`, `amount_due`, `due_at`, `paid_at`, `missed`)
VALUES
(1, 1, 100, '2013-09-17', NULL, NULL),
(3, 2, 100, '2013-09-17', NULL, NULL),
(5, 1, 100, '2013-10-17', NULL, NULL),
(7, 1, 100, '2013-11-17', NULL, NULL)
;
UPDATE loan_payments AS l
LEFT OUTER JOIN (SELECT loan_id, MIN(ID) AS ID
FROM loan_payments
WHERE DATEDIFF(NOW(), due_at) < 0
GROUP BY loan_id) AS l2 ON l.loan_id = l2.loan_id
LEFT OUTER JOIN loan_payments AS l3 ON l2.id = l3.id
SET l.missed = 1, l3.amount_due = l3.amount_due + l.amount_due
WHERE DATEDIFF(NOW(), l.due_at) >= 10
AND l.paid_at IS NULL
;
Query 1:
SELECT *
FROM loan_payments
Results:
| ID | LOAN_ID | AMOUNT_DUE | DUE_AT | PAID_AT | MISSED |
|----|---------|------------|------------|---------|--------|
| 1 | 1 | 100 | 2013-09-17 | (null) | 1 |
| 3 | 2 | 100 | 2013-09-17 | (null) | 1 |
| 5 | 1 | 200 | 2013-10-17 | (null) | (null) |
| 7 | 1 | 100 | 2013-11-17 | (null) | (null) |
Unfortunately I don't have time at the moment to write out full-blown SQL, but here's the psuedocode I think you need to implement:
select all DISTINCT loan_id from table loan_payments
for each loan_id:
set missed = 1 for all outstanding payments for loan_id (as determined by date)
select the sum of all outstanding payments for loan_id
add this sum to the amount_due for the loan's next due date after today
Refer to this for how to loop using pure MySQL: http://dev.mysql.com/doc/refman/5.7/en/cursors.html
I fixed my own problem by adding a missed_at field. I put the current timestamp ($now) in a variable before I update the first row to missed = 1 and missed_at = $now then I ran this query to update the next row's amount_due:
UPDATE loan_payments lp1 JOIN loan_payments lp2 ON lp1.due_at > lp2.due_at
SET lp1.amount_due = lp2.amount_due + lp1.amount_due
WHERE lp2.missed_at = $now AND DATEDIFF(lp1.due_at, lp2.due_at) <= DAYOFMONTH(LAST_DAY(lp1.due_at))
I wish I could use just use LIMIT 1 to that query but it turns out that it's not possible for an UPDATE query with a JOIN.
So all in all, I used two queries to achieve what I want. It did the trick.
Please advise if you have better solutions.
Thanks!

Mysql query data transformation

I am trying to do transformation on a table in Mysql. I can't figure out how to do it. Could anyone tell me how to do it? The input and output is given. I would like to know how it is done?
Input table
+-------------+------------+------------------+-------------------+
| Employee_ID | Start_Date | Termination_Date | Performance_Level |
+-------------+------------+------------------+-------------------+
| 1 | 1/1/2007 | 3/1/2007 | Low |
| 2 | 6/5/2004 | Null | Medium |
| 3 | 4/3/2003 | Null | High |
| 4 | 9/1/2002 | 4/15/2007 | Medium |
| 5 | 4/6/2007 | 11/1/2007 | Low |
| 6 | 7/1/2007 | Null | High |
| 7 | 3/2/2005 | 8/1/2007 | Low |
+-------------+------------+------------------+-------------------+
Ouput Table
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Period | Total_Employees_at_end_of_quarter | High_Performers | Medium_Performers | Low_Performers |
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Q1-2007 | 4 | 1 | 2 | 1 |
| Q2-2007 | 4 | 1 | 1 | 2 |
| Q3-2007 | 4 | 2 | 1 | 1 |
| Q4-2007 | 3 | 2 | 1 | 0 |
+---------+-----------------------------------+-----------------+-------------------+----------------+
This is what I tried
select * from emp
where date(sdate)< date'2007-04-01' and (date(tdate)> date'2007-03-31' or tdate is null);
select * from emp
where date(sdate)< date'2007-07-01' and (date(tdate)> date'2007-06-30' or tdate is null);
select * from emp
where date(sdate)< date'2007-010-01' and (date(tdate)> date'2007-09-30' or tdate is null);
select * from emp
where date(sdate)< date'2008-01-01' and (date(tdate)> date'2007-12-31' or tdate is null);
I have the individual queries but I want a single query which will give the outputs.
The approach taken below is to create a driver table for each quarter, with information about the year and quarter. This is then joined to the employee table, using a non-equijoin. Employees who start in or before the quarter and end after the quarter are active at the end of quarter.
It uses one trick for the date comparisons, which is to convert the year-quarter combination into a quarter count, by multiplying the year by 4 and adding the quarter. This is a convenience for simplifying the date comparisons.
select driver.qtryr, count(*) as TotalPerformers,
sum(Performance_level = 'High') as HighPerformers,
sum(Performance_level = 'Medium') as MediumPerformers,
sum(Performance_level = 'Low') as LowPerformers
from (select 2007 as yr, 1 as qtr, 'Q1-2007' as qtryr union all
select 2007 as yr, 2 as qtr, 'Q2-2007' as qtryr union all
select 2007 as yr, 3 as qtr, 'Q3-2007' as qtryr union all
select 2007 as yr, 4 as qtr, 'Q4-2007' as qtryr
) driver left outer join
Table1 emp
on year(emp.start_date)*4+quarter(emp.start_date) <= driver.yr*4+qtr and
(emp.termination_date is null or
year(emp.termination_date)*4+quarter(emp.termination_date) > driver.yr*4+qtr
)
group by driver.qtryr
sqlfiddle
try this
SELECT QUARTER('2008-04-01');
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_quarter
and CONCAT()