I want to display registered users each month on my web app. To achieve this I suppose to query the count from the user's table and send it to the front end. As an example, I get the count of users in one month from the following query.
select count(u_id) AS 'Count', MONTH(reg_date) AS Month
from user
WHERE(reg_date) BETWEEN '2019-10-01' AND '2019-10-31'
This displays the number of users registered in October. The resulting table I get from this query is this.
------------------------
Count Month
------------------------
2 10
------------------------
The result I want to get is the following one.
------------------------
Count Month
------------------------
2 1
------------------------
10 2
------------------------
5 3
------------------------
I have stored the user ID and register date in the user table. register date is SQL date type and UserID is Integer. I can get the number of users in a given month using the query I have mentioned below and store it in a variable in backend and call again database to get the next month and so on(Using a loop). Then create a list and send to the frontend. But for this I have to call the database several times. For getting the users of 12 months I have to query 12 times.
What I want is to get the number of users in each month from just one query. If I store the date as three columns like date, month, year I can get this result. But I do not wish to change the current table structure. Is there any way to approach this task without changing the current table sturcture?
What you are looking for is GROUP BY.
SELECT count(u_id) AS 'Count', MONTH(reg_date) AS Month
from user
WHERE(reg_date) BETWEEN '2019-01-01' AND '2019-12-31'
GROUP BY YEAR(reg_date), MONTH(reg_date);
First is to generate months 1 to 12, then join your original query based on month.
select t1.monthNo, t2.Count
from (select 1 as monthNo
union
select 2 as monthNo
union
select 3 as monthNo
union
select 4 as monthNo
union
select 5 as monthNo
union
select 6 as monthNo
union
select 7 as monthNo
union
select 8 as monthNo
union
select 9 as monthNo
union
select 10 as monthNo
union
select 11 as monthNo
union
select 12 as monthNo) as t1
left join
(select count(u_id) AS 'Count', month(reg_date) AS Month
from user
group by month(reg_date)) t2 on t2.Month = t1.monthNo
Related
I have a database that has payments called PrePay. Customers pay 12 months in one payment.
I need to have this single payment show up as 12 payments (12 records).
Here's my query:
SELECT account, startdate, servdef
FROM PAYHIST
WHERE account = 5543
startdate is a date field
This shows the 3 payments over 3 years:
I would like to produce 11 payments for each account and servdef after the first payment. So the 06-22-2018 would have 11 records following it with the months to follow i.e.
Can you please tell me how to produce the records like this?
Join with a table that returns the number of months you want to expand each row to.
SELECT account, DATE_ADD(startdate, INTERVAL n MONTH) AS startdate, servdev
FROM PAYHIST
CROSS JOIN (
SELECT 0 AS n
UNION
SELECT 1
UNION
SELECT 2
...
UNION
SELECT 11) AS q
WHERE account = 5543
ORDER BY startdate, n
Here's my "customers" table:
To get number of enquiries per for a particular month and year, I'm using following query:
SELECT YEAR(customer_date) AS Year, MONTH(customer_date) AS Month, COUNT(customer_id) AS Count FROM customers WHERE customer_product = 6 GROUP BY YEAR(customer_date), MONTH(customer_date)
I get following result:
You can see that as there is no enquery in the April month, so no row fetched for month number 4. But I want 0 value in Count column if there is no record found in that particular month and year.
This is what I want:
One option uses a calendar table to represent all months and years, even those which do not appear in your data set:
SELECT
t1.year,
t2.month,
COUNT(c.customer_id) AS Count
FROM
(
SELECT 2017 AS year UNION ALL
SELECT 2018
) t1
CROSS JOIN
(
SELECT 1 AS month UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12
) t2
LEFT JOIN customers c
ON t1.year = YEAR(c.customer_date) AND
t2.month = MONTH(c.customer_date)
WHERE
c.customer_product = 6
GROUP BY
t1.year,
t2.month
ORDER BY
t1.year,
t2.month;
Note: The above query can probably be made faster by actually creating dedicated calendar tables in your MySQL schema.
The following index on the customers table might help:
CREATE INDEX idx ON customers(customer_product, customer_id);
This might make the join between the calendar tables and customers faster, assuming that the customer_product = 6 condition is restrictive.
So I am a bit stumped, I know how to do this theoretically but I am having trouble executing it in practice.
Basically I have a table and a revisions table. The table reflects the status as of now and the revisions table reflects the past status of the table.
id3, id2, id1, title, timestamp, status,
56456 229299 4775 x name 1432866912 0
56456 232054 123859 x name 1434000054 1
56456 235578 16623 x name 1435213281 1
56456 237496 139811 x name 1464765447 1
56456 381557 0 x name 1487642800 1
56456 616934 186319 x name 1496103368 1
56456 668046 246292 x name 1505386262 1
56456 766390 246292 x name 1523273582 1
Basically what I want is to look at the historical live/offline status of all entries in the table. So I know the current status is live, and I know the dates the entry was offline/live as well.
What I want to do is calculate the live or offline dates between the timestamps.
The dates between 1 -> 0 Are live dates. The dates between 1 -> Are live dates. The dates Between 0 -> 1 Are offline dates and the dates between 0 -> 0 Are offline dates.
So ideally my data would have a live/offline status delineated by each day in between each of these status changes.
I.E
The the output would display the dates between Timestamp 1432866912 & 1434000054 as the Status being Offline
I tried searching but didn't see anything relevant.
EDIT:
#RaymondNijland The first row has a unixtimestamp for the date May 28, 2015 & the second row a timestamp of the date June 11, 2015. The first row is offline and the second row is live.
So I basically want my data to look like this
Date Status
May 28, 2015 Offline
May 29, 2015 Offline
May 30, 2015 Offline
....
....
June 9, 2015 Offline
June 10, 2015 Offline
June 11, 2015 Live
June 12, 2015 Live
I need to do it this way because our database doesn't store the data on a daily basis, but only when a change is made to the data.
Use "case" to know if it is Offline or Live and "date_format" function to display timestamp in date. See this demo: http://sqlfiddle.com/#!9/88281f/13
select DATE_FORMAT(FROM_UNIXTIME(`timestamp`), '%b %e, %Y') AS `date` ,
case when status=0 then 'Offline' else 'Live' end as `status`
from yourTbl
order by `timestamp`
You can't retrieve records that aren't in your table. In such cases, you must generate the dates sequence and then perform cross-checks or left joins etc.
have a look at
Generating Date sequence
Below code generates list of dates using min and max dates from your revision table. Do a cross check with your revision table and get the last seen/found status code for the current row date.
assuming your table is called Revisions with status and timestamp fields.
following SQL code should work for you:
Fiddle here
select
TDates.genDate, -- generated date sequence
(select case when r.status =0 then 'Offline' else 'Live' end
from revisions R
WHERE date(from_unixtime(R.Timestamp)) <= TDates.genDate
order by R.timestamp desc
limit 1
) as genStatus
from
(
SELECT *
FROM
(select adddate(T4.minDate, t3*1000 + t2*100 + t1*10 + t0) genDate, T4.minDate, T4.maxDate from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
-- using your table get the min date and max date we are going to check. So we generate just the required dates.
(select date(from_unixtime(min(R.timestamp))) as minDate, date(from_unixtime(max(R.timestamp))) as maxDate from revisions as R ) T4
) T
where T.genDate <= T.maxDate
) As TDates
order by TDates.genDate
this is just concept, you are more than welcome to improve performance hints
I am trying to create a query for a bar-chart which displays a monthly overview of the number of orders.
The query I am using is correctly providing me with the breakdown per month but when I skipped a month, it is not providing a 0 for that month, just doesnt add it at all.
Since this chart is expecting 12 numbers, everything after the missing month would be off an inaccurate.
Current Attempt:
select Month(dateCreated) as monthID,
Monthname(dateCreated) as monthName,
count(dateCreated) as totalRewards
from reward
where Year(dateCreated) = '2018'
GROUP BY monthID
If we were to assume that it is currently May 2018, I would like to see Jan - May, current counts even if the month had no orders (April = 0).
Whats the best way to include all months that have happened so far in the provided year and then their appropriate count?
You can mock a months table, then LEFT JOIN the reward table against it. To ensure you only get valid results, it's best to use a SUM() where not null rather than a COUNT() aggregate:
SELECT
months.id as monthID,
MONTHNAME(CONCAT('2018-',months.id,'-01')) as monthName,
SUM(CASE WHEN dateCreated IS NULL THEN 0 ELSE 1 END) as totalRewards
FROM
(
SELECT 1 AS id
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) as months
LEFT JOIN reward
ON MONTH(reward.dateCreated) = months.id
AND YEAR(dateCreated) = '2018'
GROUP BY monthID, monthName
ORDER BY monthID;
SQL Fiddle
Been trying to sort this one out for a while. I'd really appreciate any help.
I've got this table where I'm getting 2 columns with date and int values respectively. The problem is that mysql skips the date values wherever the int value is null.
Here the sql statement
SELECT DATE_FORMAT(sales_date_sold, '%b \'%y')
AS sale_date, sales_amount_sold
AS sale_amt
FROM yearly_sales
WHERE sales_date_sold BETWEEN DATE_SUB(SYSDATE(), INTERVAL 2 YEAR) AND SYSDATE()
GROUP BY YEAR(sales_date_sold), MONTH(sales_date_sold)
ORDER BY YEAR(sales_date_sold), MONTH(sales_date_sold) ASC;
There aren't any values for feb 2011 so that month gets skipped, along with a few others. Coalesce and if_null don't work too.
You need a row source that provides values for all of the months in the dimension, and then left join your yearly_sales table to that.
You are doing a GROUP BY, you most likely want an aggregate on your measure (sales_amount_sold), or you don't want a GROUP BY. (The query in your question is going to return a value from sales_amount_sold for only one row in a given month. That may be what you want, but its a very odd resultset to return.)
One approach is to have a "calendar_month" table that contains DATE values all of the months you want returned. (There are other ways to generate this, existing answers to questions elsewhere on stackoverflow)
SELECT m.month AS sale_date
, IFNULL(SUM(s.sales_amount_sold),0) AS sale_amt
FROM calendar_months m
LEFT
JOIN yearly_sales s
ON s.sales_date_sold >= m.month
AND s.sales_date_sold < DATE_ADD(m.month,INTERVAL 1 MONTH)
WHERE m.month BETWEEN DATE_SUB(SYSDATE(), INTERVAL 2 YEAR) AND SYSDATE()
GROUP BY m.month
ORDER BY m.month
This query returns a slightly different result, you are only going to get rows in groups of "whole months", rather than including partial months, as in your original query, because the WHERE clause on sale_date references two years before the current date and time, rather than the "first of the month" two years before.
A calendar_months table is not necessarily required; this could be replaced with a query that returns the row source. In that case, the predicate on the month value could be moved from the outer query into the subquery.
Addendum: if you use a calendar_month table as a rowsource, you'll need to populate it with every possible "month" value you want to return.
CREATE TABLE calendar_month
(`month` DATE NOT NULL PRIMARY KEY COMMENT 'one row for first day of each month');
INSERT INTO calendar_month(`month`) VALUES ('2011-01-01'),('2011-02-01'),('2011-03-01')
As an alternative, you can specify a dynamically generated rowsource, as an inline view, rather than a table reference. (You could use a similar query to quickly populate a calendar_months table.)
You can wrap this query in parenthesis, and paste it between FROM and calendar_months in the previous query I provided.
SELECT DATE_ADD('1990-01-01',INTERVAL 1000*thousands.digit + 100*hundreds.digit + 10*tens.digit + ones.digit MONTH) AS `month`
FROM ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 ) ones
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 ) tens
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 ) hundreds
JOIN ( SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 ) thousands
The problem is not that the value is NULL, the problem is that you are selecting data off your Database. If you don't have data for a specific month, MySQL has no way of selecting data that is not there.
The only way to solve this completely in MySQL is already answered in a very similar question
I have had this problem before with timestamps. The solution I used was to create a reference table with all of your months. This could be a table with just the numbers 1-12 (12 rows) or you could go one step further and put the month names. Then you can left join your yearly_sales table to the 1_through_12 table to get every month.
Why don't you just use 0 instead of NULL?