Select all years from 2 date colums - mysql

it's very simple what I'm trying to do, but no sure if it's posible and if it is how can I do it?
I have a mysql data base with a table what has trackId, StartDate, EndDate and im trying to get all the distinc years from both colums in 1 resultset.
So far I have this:
SELECT DISTINCT YEAR(StartDate) as year, YEAR(EndDate) as year
from TRACK
and my result is :
| year | year |
|------|------|
| 2016 | 2017 |
| 2017 | 2018 |
And what I'm trying to get is:
| year |
|------|
| 2016 |
| 2017 |
| 2018 |
Is it posible?

Use UNION
SELECT * from (
SELECT YEAR(StartDate) as year from TRACK
UNION
SELECT YEAR(EndDate) as year from TRACK
)
ORDER BY year
The outer select is only necessary if you need the result in an order way

you can use this:
SELECT DISTINCT [Year] From
(SELECT StartYear AS [Year] FROM #Test
UNION
SELECT EndYear AS [Year] FROM #Test) A

Related

MySQL select count only new id's for each year

I have a MySQL table that looks like this
id | client_id | date
--------------------------------------
1 | 12 | 02/02/2008
2 | 15 | 12/06/2008
3 | 23 | 11/12/2008
4 | 12 | 18/01/2009
5 | 12 | 03/03/2009
6 | 18 | 02/07/2009
7 | 23 | 08/09/2010
8 | 18 | 02/10/2010
9 | 21 | 30/11/2010
What I am trying to do is get the number of new clients for each year. 2008 has 3 new clients(12,15,23), 2009 has 1 new client(18) and 2010 has 1 new client(21).
So far I have this query that gives me the distinct clients for each year, that is 3 for 2008, 2 for 2009 and 3 for 2010.
SELECT COUNT(DISTINCT client_id) FROM table GROUP BY YEAR(date)
Any help would be appreciated..
You could use a subquery to get the first year of every client_id grouped by client_id, and then count the occurrence of client_id grouped by year, so:
SELECT COUNT(client_id), YEAR_MIN FROM (
SELECT client_id, MIN(YEAR(date)) AS YEAR_MIN
FROM table
GROUP BY client_id) AS T
GROUP BY YEAR_MIN
SQL Fiddle here
So you want to count the first date a client appears in the table. In other words, the row for which no other row exists with an earlier date and the same client. You can do this with an exclusion join.
Then you can count them per year as you're doing now.
SELECT YEAR(t.date) AS yr, COUNT(t.client_id) AS client_count
FROM (
SELECT t1.client_id, t1.date
FROM mytable AS t1
LEFT JOIN mytable AS t2 ON (t1.client_id=t2.client_id AND t1.date > t2.date)
WHERE t2.client_id IS NULL) AS t
GROUP BY yr
You should store dates using the DATE data type, which uses YYYY-MM-DD format. You won't be able to do > comparisons if your dates are stored as strings in DD-MM-YYYY format.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,client_id INT NOT NULL
,date INT NOT NULL
);
INSERT INTO my_table VALUES
(1,12,2008),
(2,15,2008),
(3,23,2008),
(4,12,2009),
(5,12,2009),
(6,18,2009),
(7,23,2010),
(8,18,2010),
(9,21,2010);
SELECT year
, COUNT(*) total
FROM
( SELECT client_id, MIN(date) year FROM my_table GROUP BY client_id ) x
GROUP
BY year;
+------+-------+
| year | total |
+------+-------+
| 2008 | 3 |
| 2009 | 1 |
| 2010 | 1 |
+------+-------+

SQL sum multiple values by date into new values by date

Hi I have reports that have been issued with some regularity but they have an initial value of accounts for when the report value is null.
I would like to create a new variable Accts_N which occurs for School, Year, and date and is the sum of that date's Accts value and the Accts value when the date is null.
So, using the sample table below, School A Year 2017 would have a Accts_N value of 8 for 2016-01-10 and a value of 12 for 2016-02-10.
School | Year | Accts | ReportDate
-------|------|-------|-----------
A | 2017 | 2 | null
A | 2017 | 6 | 2016-01-10
A | 2017 | 10 | 2016-02-10
A | 2018 | 0 | 2016-01-10
A | 2018 | 4 | 2016-02-10
B | 2017 | 9 | null
B | 2018 | 3 | 2016-2-10
I've tried a few different instances of SUM CASE WHEN but I don't think that's the right approach. Can someone suggest a direction for me?
Thank you
If you want to add a new column, then a correlated subquery comes to mind:
select r.*,
(select sum(r2.accts)
from reports r2
where r2.school = r.school and
r2.year = r.year and
(r2.reportdate = r.reportdate or r2.reportdate is null)
) as accts_n
from reports r;
How about this?
declare #t table(School nvarchar(50), Year datetime, Accts int, ReportDate datetime)
insert into #t
values
('A','2017',2,null),
('A','2017',6,'2016-01-10'),
('A','2017',10,'2016-02-10'),
('A','2018',0,'2016-01-10'),
('A','2018',4,'2016-02-10'),
('B','2017',9,null),
('B','2018',3,'2016-01-10')
select t.School, t.Year, t.ReportDate, t.Accts + ISNULL(tNulls.SumAcctsWhenNull,0)
from #t t
outer apply (select t2.School, t2.Year, SUM(Accts) AS SumAcctsWhenNull
from #t t2
where
t2.ReportDate IS NULL AND
t2.School = t.School AND
t2.Year = t.Year
group by t2.School, t2.Year) tNulls
where
t.ReportDate IS NOT NULL

How to group by month and return zero if no value for certain month?

This is my mysql income table.
+----+------------------+---------------------------+------------+---------+
| id | title | description | date | amount |
+----+------------------+---------------------------+------------+---------+
| 1 | Vehicle sales up | From new sale up | 2016-09-09 | 9999.99 |
| 2 | Jem 2 Sales | From rathnapura store | 2016-05-15 | 9545.25 |
| 3 | Jem 2 Sales 2 | From rathnapura store | 2016-05-15 | 9545.25 |
| 4 | Jem 2 Sales 2 | From rathnapura store 234 | 2016-05-15 | 9545.25 |
+----+------------------+---------------------------+------------+---------+
The field 'date' is standard sql date. And I executed this query in order to take sum of incomes by month and return zero if no income from a certain month. I want zeros if no income from a certain month because i want to display these data in a chart.
This is the query.
SELECT MONTHNAME(`date`) AS mName, MONTH(`date`) AS mOrder, ifnull(sum(amount),0) AS total_num FROM income GROUP BY mOrder ORDER BY mOrder DESC
But I only get a output like follows. No zeros if no values in other months. This is the output.
+-----------+--------+-----------+
| mName | mOrder | total_num |
+-----------+--------+-----------+
| September | 9 | 9999.99 |
| May | 5 | 28635.75 |
+-----------+--------+-----------+
And I want other months in above table and total_num as zero. How can I do this? There's same kind of question there too. But no working answer.
Group by month and return 0 if data not found
Please help me to solve this issue. The language I use for this application is Node.JS :)
Have a table of all the months and then left join to your table:
SELECT MONTHNAME(m.month) AS mName,
MONTH(m.month) AS mOrder,
ifnull(sum(amount),0) AS total_num
from months m
left join income i
on m.month = i.date
GROUP BY mOrder
ORDER BY mOrder DESC
If you don't want to create a months table then you can:
(select STR_TO_DATE('01/01/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/02/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/03/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/04/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/05/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/06/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/07/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/08/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/09/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/10/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/11/2016', '%d/%m/%Y') as month union
select STR_TO_DATE('01/12/2016', '%d/%m/%Y') as month)
You should create a CALENDAR table, with the precision you need, in this case months.
+-----------+
| Month |
+-----------+
| January |
| February |
.......
And Join on it
Maybe this it's not the best way to do it, but it will solve your problem. As a quick soution:
SELECT 'January' AS mName, 1 AS mOrder, COALESCE(SUM(amount),0) AS total_num
FROM income i
WHERE month(i.date) = 1
UNION
SELECT 'February' AS mName, 2 AS mOrder, COALESCE(SUM(amount),0) AS total_num
FROM income i
WHERE month(i.date) = 2
UNION
...and go on

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

sql to select one record for every month with a sum of that months records

I have a database that has an "appointments table" and "services table". Each appt has a service and each service has a price. What I would like is one query, that will always return 12 rows (one row for each month) and contain a sum of the months appts (based on it's service id). So far I have:
select sum(service_price) as monthly_total,
year(appt_date_time) as year,
monthname(appt_date_time) as month
from appt_tbl
join services_tbl on appt_tbl.service_id = services_tbl.service_id
group by month(appt_date_time),
year(appt_date_time)
order by month(appt_date_time) asc;
Currently, this returns something like:
+---------------+------+-------+
| monthly_total | year | month |
+---------------+------+-------+
| 120.00 | 2012 | July |
+---------------+------+-------+
The problem is that if a month doesn't have any appts, I do not get that month returned in the query. I would like that month to have a record, just have it's "monthly_total" equal zero.
Below is what I would like the query to return:
+---------------+------+-------+
| monthly_total | year | month |
+---------------+------+-------+
| 0.00 | 2012 | Jan |
+---------------+------+-------+
| 0.00 | 2012 | Feb |
+---------------+------+-------+
| 0.00 | 2012 | March |
+---------------+------+-------+
| 0.00 | 2012 | April |
+---------------+------+-------+
| 0.00 | 2012 | May |
+---------------+------+-------+
| 0.00 | 2012 | June |
+---------------+------+-------+
| 120.00 | 2012 | July |
+---------------+------+-------+
| 0.00 | 2012 | August|
+---------------+------+-------+
| 0.00 | 2012 | Sept |
+---------------+------+-------+
| 0.00 | 2012 | Oct |
+---------------+------+-------+
| 0.00 | 2012 | Nov |
+---------------+------+-------+
| 0.00 | 2012 | Dec |
+---------------+------+-------+
Any ideas?
You're on the right track, just LEFT JOIN the result of your query to a table of monthnames. Then the names that exist in your result will return a match, and a NULL will be returned for those months that are not in your result. A COALESCE or NVL or CASE construct, whatever you like, can be used to turn that NULL into a straight 0.
You don't need make a real table of monthts, you can get by using an inline view - simply a query that generates all the month data.
select months.month, coalesce(appts.monthly_total, 0) monthly_total
from (
select 'January' as month
union all select 'February'
union all select 'March'
...etc...
union all select 'December'
) months
left join (
select sum(service_price) as monthly_total,
monthname(appt_date_time) as month
from appt_tbl
inner join services_tbl
on appt_tbl.service_id = services_tbl.service_id
where appt_date_time between '2012-01-01 00:00:00'
and '2012-12-31 23:59:59'
group by month(appt_date_time)
) appts
on months.month = appts.month
As for returning everyting for a particular year, look at the WHERE condition. I am asuming appt_date_time is a datetime or timestamp. You don't want to write YEAR(appt_date_time) = 2012 because that will ruin the chances of using an index on that column (I am assuming that column appears as a first column in an index). By using a BETWEEN...AND and comparing against literal datetimes, you can have a pretty efficient query that meets the requirement.
Also, if you GROUP BY on month(appt_date_time) mysql will ensure results will by sorted ascending by month too. So no need for a separate ORDER BY. The order of the 'legs' of the UNION query will also be preserved by mysql.
Try this, it will work for you. All you need to do is to create a lookup table for better approach.
CREATE TABLE MONTHS(MON CHAR(3))
INSERT INTO MONTHS
SELECT 'Jan' UNION ALL
SELECT 'Feb' UNION ALL
SELECT 'Mar' UNION ALL
SELECT 'Apr' UNION ALL
SELECT 'May' UNION ALL
SELECT 'Jun' UNION ALL
SELECT 'Jul' UNION ALL
SELECT 'Aug' UNION ALL
SELECT 'Sep' UNION ALL
SELECT 'Oct' UNION ALL
SELECT 'Nov' UNION ALL
SELECT 'Dec'
CREATE TABLE Appointments(App int, Year int, Month CHAR(3))
INSERT INTO Appointments
SELECT 120,2012,'Jul' UNION ALL
SELECT 120,2013,'Apr'
SELECT sum(isnull(App,0)) monthly_total, b.Mon , b.Year FROM Appointments a
RIGHT JOIN (SELECT DISTINCT Year,Mon FROM Appointments CROSS JOIN MONTHS) b
ON a.Month=b.MON AND a.Year=b.Year
GROUP BY b.Mon,b.Year