Mysql count and group between specific dates - php

I have a problem with a query.
I have a table like this:
tbl_people
id | startDate | endDate | Gender
1 | 2010-03-01 | 2011-04-11 | m
2 | 2010-04-01 | 2010-06-14 | f
3 | 2010-03-01 | 2010-04-11 | m
4 | 2009-09-09 | 2009-10-10 | f
For all years given in the database I want to count the gender of the people, for who that year is between startDate and endDate. When startDate is 2010 and endDate 2011 it should count for both.
So the result should look like this:
year | m | f | m+f
2009 | 0 | 1 | 1
2010 | 2 | 1 | 3
2011 | 1 | 0 | 1
I have no really good idea how to realize that query for a list of all years.
Currently I have this:
select
sum(case tbl_people.Gender when 'm' then 1 else 0 end),
sum(case tbl_people.Gender when 'f' then 1 else 0 end),
count( tbl_people.Gender )
...
Best regards

You need to join with a table that contains all the years. You can either create a real table with all the years, or construct it on the fly in a subquery:
SELECT y.year,
SUM(p.Gender = 'm') AS m,
SUM(p.Gender = 'f') AS f,
COUNT(*) AS `m+f`
FROM (SELECT 2009 AS year
UNION
SELECT 2010 AS year
UNION
SELECT 2011 AS year) AS y
LEFT JOIN tbl_people AS p ON y.year BETWEEN YEAR(p.startDate) AND YEAR(p.endDate)
GROUP BY y.year
DEMO

Related

Eloquent SELECT from UNION result

I have two different tables with the (almost) the same structure:
(not the best DB design, but unfortunately out of my control)
Table A:
+-----------+------+-------+-----+
| client_id | year | month | fee |
+-----------+------+-------+-----+
| 33 | 11 | 2022 | 11 |
| 42 | 11 | 2022 | 13 |
| 33 | 12 | 2022 | 27 |
| 16 | 12 | 2022 | 15 |
+-----------+------+-------+-----+
Table B:
+-----------+------+-------+-----+
| client_id | year | month | fee |
+-----------+------+-------+-----+
| 33 | 11 | 2022 | 19 |
| 57 | 11 | 2022 | 34 |
+-----------+------+-------+-----+
I want to SUM fees of all clients grouping bu month and year.
For one table the eloquent query is simple:
$queryA = DB::table('A')
->selectRaw('month, year, COUNT(*) AS total, SUM(fee) AS total_fee')
->groupBy(['month', 'year']);
($queryB will be the same, with ->table('B') instead ).
In order to get total sum of both tables, I want to use UNION.
The approach:
I want to apply a SELECT query on the result returned by UNION:
SELECT month, year, COUNT(*) AS total, SUM(total_fee) AS total_fee
FROM
(
(
SELECT month, year, COUNT(*) AS total, SUM(fee) AS total_fee
FROM A
GROUP BY month, year
) UNION (
SELECT month, year, COUNT(*) AS total, SUM(fee) AS total_fee
FROM B
GROUP BY month, year
)
) AS result_table
GROUP BY month, year
ORDER BY year DESC, month DESC;
The problem:
I tried this:
$queryA
->union($queryB)
->select(month, year, COUNT(*) AS total, SUM(total_fee) AS total_fee)
->groupBy(['month', 'year'])
the new SELECT statement is inserted into the $queryA sub-query.
Any idea how to achieve what I am looking for?

How to use subquery with group by in mysql?

I have one table with multiple rows for particular users.i am having
data with many years like 2019,2018,2016 and more. i have two
scenarios:
1.i want data of particular INVOICE_YEAR.
2,but same time i want FIRST DATE OF INVOICE GENERATED FOR PARTICULAR
USER.
my sql query :
$yearOf this is dynamic year input variable.
$yearOf = 2019;
$Records = "SELECT MIN(inv.INVOICE_DATE) AS MIN_INVOICE_DATE
FROM invoices as inv
LEFT JOIN customers as cm ON cm.CUSTOMER_ID = inv.CUSTOMER_ID
where inv.INVOICE_YEAR IN (".$yearOf.")
group by inv.CUSTOMER_ID ORDER BY cm.CUSTOMER_NAME ASC";
As you can see my query if i want data of all users where INVOICE_YEAR IN ('2019').My first condition will satisfied i will get all data of users where INVOICE_YEAR = 2019.
But same time i want first invoice date so for this i used MIN(inv.INVOICE_DATE) but as i used
where inv.INVOICE_YEAR IN (".$yearOf.") this in where condition so it gives me first invoice date of particular year.
But i want first invoice date from whole table for all users.
I tried with subquery but it shows me error of Subquery returns more than 1 row
My query with subquery:
$Records = "SELECT
(
SELECT MIN(inv.INVOICE_DATE) AS MIN_INVOICE_DATE FROM invoices AS inv GROUP BY inv.CUSTOMER_ID) AS MIN_INVOICE_DATE
FROM invoices as inv
LEFT JOIN customers as cm ON cm.CUSTOMER_ID = inv.CUSTOMER_ID
where inv.INVOICE_YEAR IN (".$yearOf.")
group by inv.CUSTOMER_ID ORDER BY cm.CUSTOMER_NAME ASC";
For eg:
3 customers are there 101,102,103
data in table is like:
id | customer_id | invoice_date | invoice_year
1 | 101 | 2019-01-01 | 2019
2 | 101 | 2016-01-01 | 2016
3 | 101 | 2017-01-01 | 2017
4 | 101 | 2016-01-02 | 2016
5 | 102 | 2019-01-02 | 2019
6 | 103 | 2018-01-02 | 2018
7 | 103 | 2019-01-07 | 2019
8 | 102 | 2015-01-02 | 2015
As i Request query to get data of INVOICE_YEAR 2019 with first invoice date of particular user so it should give output like :
id | customer_id | invoice_date | invoice_year | min_invoice_date
1 | 101 | 2019-01-01 | 2019 | 2016-01-01
5 | 102 | 2019-01-02 | 2019 | 2015-01-02
7 | 103 | 2019-01-07 | 2019 | 2019-01-07
With as i want first invoice date IN COLUMN MIN_INVOICE_DATE of all users.
But it shows me data like :
id | customer_id | invoice_date | invoice_year | min_invoice_date
1 | 101 | 2019-01-01 | 2019 | 2019-01-01
5 | 102 | 2019-01-02 | 2019 | 2019-01-02
7 | 103 | 2019-01-07 | 2019 | 2019-01-07
You want to pull out the earliest invoice for each customer in 2019, along with the date of their earliest invoice within the whole table.
In MySQL 8.0, you can solve this using window functions:
SELECT id, customer_id, invoice_date, invoice_year, min_invoice_date
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY customer_id, invoice_year ORDER BY invoice_date) rn,
MIN(invoice_date) OVER(PARTITION BY customer_id) min_invoice_date
FROM mytable t
) x
WHERE invoice_year = 2019 AND rn = 1
In earlier versions, you can:
JOIN the table with a query that computes the overall minimum invoice_date per customer
use a correlated subquery with a NOT EXISTS condition to filter on the earliest invoice par customer in 2019
Query:
SELECT t.id, t.customer_id, t.invoice_date, t.invoice_year, m.min_invoice_date
FROM mytable t
INNER JOIN (
SELECT customer_id, MIN(invoice_date) min_invoice_date
FROM mytable
GROUP BY customer_id
) m ON m.customer_id = t.customer_id
WHERE
t.invoice_year = 2019
AND NOT EXISTS (
SELECT 1
FROM mytable t1
WHERE t1.invoice_year = 2019
AND t1.customer_id = t.customer_id
AND t1.invoice_date < t.invoice_date
)
In this demo on DB Fiddle, both queries return:
| id | customer_id | invoice_date | invoice_year | min_invoice_date |
| --- | ----------- | ------------ | ------------ | ---------------- |
| 1 | 101 | 2019-01-01 | 2019 | 2016-01-01 |
| 5 | 102 | 2019-01-02 | 2019 | 2015-01-02 |
| 7 | 103 | 2019-01-07 | 2019 | 2018-01-02 |
I guess you want the to see the details of the first (earliest-dated) invoice for each customer in each calendar year. You want that result filtered to cover only one year.
So, start with a subquery to find the date of the first invoice for every customer in each calendar year. (https://www.db-fiddle.com/f/bmBZ14Vr9Re6ahpfs2FF2X/0)
SELECT MIN(invoice_date) first_invoice_date,
YEAR(invoice_date) calendar_year,
customer_id
FROM invoices
GROUP BY YEAR(invoice_date), customer_id
Then retrieve the detail for those invoices by JOINing that subquery to your original invoices table. (https://www.db-fiddle.com/f/bmBZ14Vr9Re6ahpfs2FF2X/1)
SELECT invoices.*
FROM invoices
JOIN (
SELECT MIN(invoice_date) first_invoice_date,
YEAR(invoice_date) calendar_year,
customer_id
FROM invoices
GROUP BY YEAR(invoice_date), customer_id
) firsts
ON invoices.customer_id = firsts.customer_id
AND invoices.invoice_date = firsts.first_invoice_date
ORDER BY invoices.customer_id,
invoices.invoice_year,
invoices.invoice_date
Then, throw in WHERE invoices.invoice_year = 2019 to get just the year you want.
Notice that your invoice_year column is unnecessary, since it can always be computed from YEAR(invoice_date). You should consider getting rid of it.

get 12 month application count form SQL and start count form previous 3 month

This my Table Structure
###Table ###Expected Outpoot
ID | create_date Month | Application | Year
------------------------------ --------------------------
1 | 2017-06-25 10:00:11 10 | 0 | 2016
2 | 2017-06-26 10:00:11 11 | 0 | 2016
3 | 2017-07-02 10:00:11 12 | 0 | 2016
4 | 2017-07-25 10:00:11 1 | 0 | 2017
5 | 2017-08-21 10:00:11 2 | 0 | 2017
6 | 2017-08-22 10:00:11 3 | 0 | 2017
7 | 2017-08-25 10:00:11 4 | 0 | 2017
5 | 0 | 2017
6 | 2 | 2017
7 | 2 | 2017
8 | 3 | 2017
9 | 0 | 2017
I am Trying to get the monthly data count from my table. I want to start the count form Previous 3 month.
This is what I have tried so far?
Edit: I am close to solve the issue.Here is the query and result.
select date_format(tn.create_date,'%Y-%m') as mon,
count(*) as num
FROM table_name as tn
GROUP BY mon order by mon;
Month | Application
--------------------------------
2017-06 | 2
2017-07 | 2
2017-08 | 3
So, How do i get my expected output?
Here is the Query Fiddle
You need a list of months to join into your query. You can make a table for it, as #JohnHC suggested or use this hack to list the months like this:
set #start='2017-06-01';
select YEAR(date), MONTH(date), COUNT(create_date) from
(
select adddate(#start, INTERVAL #num:=#num+1 MONTH) date
from test, (select #num:=-9) num
limit 12
) as dt
LEFT JOIN test ON MONTH(create_date) = MONTH (date) and YEAR(create_date) = YEAR(date)
GROUP BY date;
Notice, that in the dt query, the actual data from the test table is not accessed, but it is required to contain at least 12 rows to work. Also, you don't need to use the same table for generating the month sequence what you use for querying the create_date.
You can set the start of the interval in the #num:=-9 expression. -9 in this case means 9 months before #start date. You can set the length of the interval in the LIMIT clause.
Example fiddle.

mysql query PHP: I want to a specific items to be first and then to sort the rest of the items

I have the table below.
id | car_name | owner
-------------------------
1 | Toyota | Jan
2 | Ford | Mike
3 | Isuzu | Andrew
4 | BMW | Jan
5 | Ferrari | Steve
6 | Audi | Jan
7 | Benz | Klark
8 | Hyundai | Jan
9 | Kia | Jan
I want to get all the car owners, but Jan has 5 cars I want the first four item of Jan to be in the list. I don't care about the order that I receive the rest of the items. Like this.
id | car_name | owner
-------------------------
1 | Toyota | Jan
4 | BMW | Jan
7 | Benz | Jan
8 | Hyundai | Jan
2 | Ford | Mike
3 | Isuzu | Andrew
5 | Ferrari | Steve
6 | Audi | Bob
9 | Kia | Jan
How can I do this? Thanks
You can order by any one of the following
order by owner <> 'Jan'
order by owner = 'Jan' desc
order by case when owner = 'Jan' then 0 else 1 end
order by if(owner = 'Jan',0,1)
owner = 'Jan' produces 1 for True and 0 for False, hence why desc
Also, since you don't care about the order of the rest of the rows except first four, you might as well keep the rest of Jan rows in continuation.
Try this:
SELECT
id, car_name, owner
FROM
((SELECT
0 x, t.*
FROM
your_table t
ORDER BY owner <> 'Jan' , id
LIMIT 4) UNION ALL (SELECT
*
FROM
(SELECT
1 x, t.*
FROM
your_table t
ORDER BY owner <> 'Jan' , id
LIMIT 4 , 1000) t
ORDER BY id)) t
ORDER BY x , id;
Works only if Jan has 4 or more rows.
SQLFiddle
SELECT * FROM `table_name` order by owner = 'Jan' DESC
Try this
SELECT * FROM cars where id not in (select id from cars where owner='Jan' and id = 9) order by owner = 'Jan' desc;

sql/php - display records horizontally and vertically

So I have this data from basic mySQL select query, that outputs per row.
-Basic mySQL select query
ID---------DATE---------POINT1---POINT2------TOTAL
1----2013-01-03----------10----------16---------26
2----2013-01-03----------11----------22---------33
3----2013-01-03----------15----------7----------22
1----2013-01-04----------20----------4----------24
2----2013-01-04----------8-----------32---------40
3----2013-01-04----------16----------12---------28
1----2013-01-05----------12----------17---------29
2----2013-01-05----------2-----------29---------31
3----2013-01-05----------8-----------10---------18
What I want to do is sort the data by date in columns and by id in rows dynamically, something like per month. here is the desired output,
/----------/2013-01-03/---------/2013-01-04/------/2013-01-05/------/
ID--Point 1-Point2-Total-Point 1-Point2-Total-Point 1-Point2-Total
1----10-------16-----26-----20-------4-----24----12-----17------29
2----11-------22-----33------8------32-----40-----2-----29------31
3----15-------7------22-----16------12-----28-----8-----10------18
then output it to csv or excel. I'm kinda lost on how I can achieve this. If someone can guide me it would be great. thanks
You can unpivot the data in your columns and then perform a pivot to transform all of the data back into columns:
select id,
sum(case when date='2013-01-03' and col='Point1' then value end) Point1_01032013,
sum(case when date='2013-01-03' and col='Point2' then value end) Point2_01032013,
sum(case when date='2013-01-03' and col='Total' then value end) Total_01032013,
sum(case when date='2013-01-04' and col='Point1' then value end) Point1_01042013,
sum(case when date='2013-01-04' and col='Point2' then value end) Point2_01042013,
sum(case when date='2013-01-04' and col='Total' then value end) Total_01042013,
sum(case when date='2013-01-05' and col='Point1' then value end) Point1_01052013,
sum(case when date='2013-01-05' and col='Point2' then value end) Point2_01052013,
sum(case when date='2013-01-05' and col='Total' then value end) Total_01052013
from
(
select id, date_format(date, '%Y-%m-%d') date, 'Point1' col, Point1 as value
from yourtable
union all
select id, date_format(date, '%Y-%m-%d') date, 'Point2' col, Point2
from yourtable
union all
select id, date_format(date, '%Y-%m-%d') date, 'Total' col, Total
from yourtable
) src
group by id;
See SQL Fiddle with Demo
The result is:
| ID | POINT1_01032013 | POINT2_01032013 | TOTAL_01032013 | POINT1_01042013 | POINT2_01042013 | TOTAL_01042013 | POINT1_01052013 | POINT2_01052013 | TOTAL_01052013 |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | 10 | 16 | 26 | 20 | 4 | 24 | 12 | 17 | 29 |
| 2 | 11 | 22 | 33 | 8 | 32 | 40 | 2 | 29 | 31 |
| 3 | 15 | 7 | 22 | 16 | 12 | 28 | 8 | 10 | 18 |

Categories