selecting distinct column name with respectives values - mysql

I have table as
P_Id | userid | year | month | day
-----+--------+------+-------+------
3 | 3 | 2011 | 2 | 2
5 | 1 | 2011 | 2 | 3
16 | 8 | 2011 | 3 | 4
5 | 3 | 2011 | 4 | 4
17 | 1 | 2011 | 4 | 6
8 | 4 | 2011 | 7 | 7
9 | 3 | 2011 | 8 | 8
10 | 8 | 2011 | 9 | 9
I want to select distinct column i.e userid but also the respective value of year month and year which were encountered first.
For given above table following should be output
P_Id | userid | year | month | day
-----+--------+------+-------+------
3 | 3 | 2011 | 2 | 2
5 | 1 | 2011 | 2 | 3
16 | 8 | 2011 | 3 | 4
8 | 4 | 2011 | 7 | 7
or
If i am ordering the table by year,month and day
userid which is encountered first must only be selected and rest must be not be selected

Put year, month and day to native date column and do this:
select p_id, userid, min(the_date) from table group by p_id, userid
It will provide the fastest result.
If you cant modify your table and should use year+month+day then you can convert this values to date and still use min function.

SELECT ta.*
FROM
( SELECT DISTINCT userid
FROM tableX
) AS di
JOIN
tableX AS ta
ON ta.P_id =
( SELECT ti.P_id
FROM tableX AS ti
WHERE ti.userid = di.userid
ORDER BY ti.year, ti.month, ti.day
LIMIT 1
)

Your query is as follows;
select * from (select min(p_id)p_id,userid, min(year)year,min(month)month,min(day)day from tsil group by userid) t order by p_id;
and here is the test;
create table tsil(p_id int, userid int, year int, month int, day int);
insert into tsil values (3,3,2011,2,2)
,(5,1,2011,2,3)
,(16,8,2011,3,4)
,(5,3,2011,4,4)
,(17,1,2011,4,6)
,(8,4,2011,7,7)
,(9,3,2011,8,8)
,(10,8,2011,9,9);
commit;
select * from (select max(p_id)p_id,userid, min(year)year,min(month)month,min(day)day from tsil group by userid) t order by p_id;
drop table tsil;
and here is the result; what you expected.
+------+--------+------+-------+------+
| p_id | userid | year | month | day |
+------+--------+------+-------+------+
| 3 | 3 | 2011 | 2 | 2 |
| 5 | 1 | 2011 | 2 | 3 |
| 8 | 4 | 2011 | 7 | 7 |
| 16 | 8 | 2011 | 3 | 4 |
+------+--------+------+-------+------+

Related

Query for total earnings

I have the following table 'collection'. It stores the sales from 2 shops in the form of cash and card:
Date | Shop | Cash | Card |
-----------------------------------
2017-01-01 | A | 10 | 5 |
2017-01-01 | B | 8 | 2 |
2017-01-02 | A | 9 | 6 |
2017-01-02 | B | 8 | 5 |
2017-01-03 | A | 9 | 7 |
2017-01-03 | B | 10 | 1 |
I want to run the SQL query and get the total daily earning from the two shops as the following output
Day | Earnings
-------------------
1 | 25
2 | 28
3 | 27
Should be easy with a simple GROUP BY like:
SELECT Date
,SUM(Cash + Card) AS Earnings
FROM yourtable
GROUP BY Date
Just Check as below :
SELECT row_number() over (order by date) AS Day
,SUM(Cash + Card) AS Earnings
FROM #TEMP
GROUP BY Date

mysql select latest record only for record

I have a MySQL database with the following structure:
custodian | counta | countc | countc | total | date
-------------------------------------------------------
ed | 1 | 2 | 3 | 6 | 1/1/2016
ed | 2 | 3 | 5 | 10 | 1/2/2016
ed | 2 | 3 | 6 | 11 | 1/3/2016
ed | 1 | 3 | 5 | 9 | 1/4/2016
fred | 1 | 2 | 3 | 6 | 1/1/2016
fred | 2 | 3 | 5 | 10 | 1/2/2016
fred | 2 | 3 | 6 | 11 | 1/3/2016
fred | 1 | 3 | 5 | 9 | 1/4/2016
How do I return the latest record for a custodian? I've been playing around with this condition where date >= DATE_SUB(NOW(),INTERVAL 59 MINUTE) since the table is updated hourly, but if I update the script twice in an hour, I would return more than one result per custodian.
Any advice?
You need to combine ORDER BY and LIMIT:
SELECT *
FROM yourTableName
WHERE custodian = 123
ORDER BY `date` DESC
LIMIT 1
You could try this
SELECT * FROM tbl ORDER BY date DESC LIMIT 1
The most recent date will be the first record when ordered decendingly, and limiting the select to 1 means you get only the latest record.

MYSQL : How to select every YEAR_MONTH between two dates?

What I want to do :
I have a table like this :
TABLE mytable
- ID (INT)
- START (DATETIME)
- END (DATETIME)
Let's say I have these rows :
| ID | START | END |
|--------------------------------------------------
| 1 | 2014-01-02 00:00:00 | 2014-12-02 00:00:00 | => month between : 12
| 2 | 2014-01-03 00:00:00 | 2015-02-03 00:00:00 | => month between : 14
Note : the "month between" include the start and end months
I for each YEAR_MONTH between START and END, I want to display a row like this :
ID | MONTH | YEAR
---------------------
1 | 1 | 2014
1 | 2 | 2014
1 | 3 | 2014
1 | 4 | 2014
1 | 5 | 2014
1 | 6 | 2014
1 | 7 | 2014
1 | 8 | 2014
1 | 9 | 2014
1 | 10 | 2014
1 | 11 | 2014
1 | 12 | 2014
2 | 1 | 2014
2 | 2 | 2014
2 | 3 | 2014
2 | 4 | 2014
2 | 5 | 2014
2 | 6 | 2014
2 | 7 | 2014
2 | 8 | 2014
2 | 9 | 2014
2 | 10 | 2014
2 | 11 | 2014
2 | 12 | 2014
2 | 1 | 2015
2 | 2 | 2015
So 12 records for ID 1 and 14 for ID 2.
I'm a bit stuck when the number of month is > 12
WHERE I AM :
I'm doing this :
SELECT mytable.id,
months.id as month,
YEAR(start) as year
FROM mytable
/* Join on a list from 1 to 12 */
LEFT JOIN (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 ON months.id BETWEEN MONTH(start) AND MONTH(end)
order by mytable.id, month, year
So ID 2 only has 2 rows for month 1 and 2 :
ID | MONTH | YEAR
---------------------
1 | 1 | 2014
1 | 2 | 2014
1 | 3 | 2014
1 | 4 | 2014
1 | 5 | 2014
1 | 6 | 2014
1 | 7 | 2014
1 | 8 | 2014
1 | 9 | 2014
1 | 10 | 2014
1 | 11 | 2014
1 | 12 | 2014
2 | 1 | 2014
2 | 2 | 2014
Do you have any ideas or advices for this problem ?
Is there a way to extract every YEAR_MONTH between two dates ?
Thank you.
HELPER :
Here is a script to create the table and insert the 2 rows mentionned :
CREATE TABLE mytable (
id INT PRIMARY KEY auto_increment,
start DATETIME NOT NULL,
end DATETIME NOT NULL
);
INSERT INTO mytable (start,end) VALUES
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00");
If I understand you correctly, you need a table with dates (year - month) between each start and end date.
There's no simple select statement that will give you this, but you can create a procedure to do it. You need to create a temporary table, fill it with the values you need and then output the result.
Here's my proposed solution (considering a permanent table):
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE mytable (
id INT PRIMARY KEY auto_increment,
start DATETIME NOT NULL,
end DATETIME NOT NULL
)//
INSERT INTO mytable (start,end) VALUES
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00")//
create procedure year_month_table()
begin
-- Declare the variables to fill the years_months table
declare id int;
declare start_date, end_date, d date;
-- Declare the "done" variable for the loop that fills the table,
-- the cursor to read the data, and the handler to check if the
-- loop should end.
declare done int default false;
declare cur_mytable cursor for
select * from mytable;
declare continue handler for not found
set done = true;
-- Create the table to hold your data
create table if not exists years_months (
row_id int unsigned not null auto_increment primary key,
id int not null,
month int,
year int,
unique index dedup(id, year, month),
index idx_id(id),
index idx_year(year),
index idx_month(month)
);
-- Open the cursor to read the ids and the start and end dates for each one
open cur_mytable;
-- Disable the indexes to speed up insertion
alter table years_months disable keys;
-- Start the loop
loop_data: loop
-- Read the values from your table and store them in the variables
fetch cur_mytable into id, start_date, end_date;
-- If you've reached the end of the table, then you must exit the loop
if done then
leave loop_data;
end if;
-- Initialize the date to fill the table
set d = start_date;
while d <= end_date do
-- Insert the values in your table
insert ignore into years_months (id, month, year) values (id, month(d), year(d));
-- Increment the d variable in 1 month
set d = date_add(d, interval +1 month);
end while;
end loop;
close cur_mytable;
-- Enable the indexes again
alter table years_months enable keys;
-- Show the result
select * from years_months;
end //
Query 1:
select * from mytable
Results:
| ID | START | END |
|----|--------------------------------|---------------------------------|
| 1 | January, 02 2014 00:00:00+0000 | December, 02 2014 00:00:00+0000 |
| 2 | January, 03 2014 00:00:00+0000 | February, 03 2015 00:00:00+0000 |
Query 2:
call year_month_table()
Results:
| ROW_ID | ID | MONTH | YEAR |
|--------|----|-------|------|
| 1 | 1 | 1 | 2014 |
| 2 | 1 | 2 | 2014 |
| 3 | 1 | 3 | 2014 |
| 4 | 1 | 4 | 2014 |
| 5 | 1 | 5 | 2014 |
| 6 | 1 | 6 | 2014 |
| 7 | 1 | 7 | 2014 |
| 8 | 1 | 8 | 2014 |
| 9 | 1 | 9 | 2014 |
| 10 | 1 | 10 | 2014 |
| 11 | 1 | 11 | 2014 |
| 12 | 1 | 12 | 2014 |
| 13 | 2 | 1 | 2014 |
| 14 | 2 | 2 | 2014 |
| 15 | 2 | 3 | 2014 |
| 16 | 2 | 4 | 2014 |
| 17 | 2 | 5 | 2014 |
| 18 | 2 | 6 | 2014 |
| 19 | 2 | 7 | 2014 |
| 20 | 2 | 8 | 2014 |
| 21 | 2 | 9 | 2014 |
| 22 | 2 | 10 | 2014 |
| 23 | 2 | 11 | 2014 |
| 24 | 2 | 12 | 2014 |
| 25 | 2 | 1 | 2015 |
| 26 | 2 | 2 | 2015 |
Notice that that last select statement in the procedure is the one that outputs the result. You can execute it every time you need.
Hope this helps
Important: As pointed by #amaster in his comment, this answer will fail if the period spans more than two years.
(Use the following code under your own risk ;) )
I've found another way to do this, but it's not a simple select statement and I think it's prone to errors, but I will put it here anyway:
select mytable.id, month, year
from mytable,
(select month, year
from
(select 1 as month
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 a,
(select year(start) as year from mytable
union select year(end) as year from mytable) as b) as a
where cast(concat_ws('-', a.year, a.month, day(mytable.start)) as date)
between date(mytable.start) and date(mytable.end)
order by mytable.id, year, month;
See this other SQL fiddle.
I know I am late to the party, but I was needing a good solution and sequencing was not working for my db version.
I started with https://stackoverflow.com/a/14813173/1707323 and made a few changes to get it working for my use like in this OP.
SELECT
DATE_FORMAT(m1, '%c') AS month_single,
DATE_FORMAT(m1, '%Y') AS this_year
FROM
(
SELECT
'2017-08-15' +INTERVAL m MONTH AS m1
FROM
(
SELECT
#rownum:=#rownum+1 AS m
from
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t4,
(SELECT #rownum:=-1) t0
) d1
) d2
WHERE
m1<='2020-03-23'
ORDER BY m1
This will get all of the months between these two dates. Please notice that the start date is in the second select clause and the end date is in the final where clause. This will include the starting month and ending month as well. It could be easily modified to exclude the starting and ending months with some extra +/- INTERVALS.

Complex SQL query suggestions please

I have three tables with schema as below:
Table: Apps
| ID (bigint) | USERID (Bigint)| START_TIME (datetime) |
-------------------------------------------------------------
| 1 | 13 | 2013-05-03 04:42:55 |
| 2 | 13 | 2013-05-12 06:22:45 |
| 3 | 13 | 2013-06-12 08:44:24 |
| 4 | 13 | 2013-06-24 04:20:56 |
| 5 | 13 | 2013-06-26 08:20:26 |
| 6 | 13 | 2013-09-12 05:48:27 |
Table: Hosts
| ID (bigint) | APPID (Bigint)| DEVICE_ID (Bigint) |
-------------------------------------------------------------
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 1 |
| 4 | 3 | 3 |
| 5 | 1 | 4 |
| 6 | 2 | 3 |
Table: Usage
| ID (bigint) | APPID (Bigint)| HOSTID (Bigint) | Factor (varchar) |
-------------------------------------------------------------------------------------
| 1 | 1 | 1 | Low |
| 2 | 1 | 3 | High |
| 3 | 2 | 2 | Low |
| 4 | 3 | 4 | Medium |
| 5 | 1 | 5 | Low |
| 6 | 2 | 2 | Medium |
Now if put is userid, i want to get the count of rows of table rows for each month (of all app) for each "Factor" month wise for the last 6 months.
If a DEVICE_ID appears more than once in a month (based on START_TIME, based on joining Apps and Hosts), only the latest rows of Usage (based on combination of Apps, Hosts and Usage) be considered for calculating count.
Example output of the query for the above example should be: (for input user id=13)
| MONTH | USAGE_COUNT | FACTOR |
-------------------------------------------------------------
| 5 | 0 | High |
| 6 | 0 | High |
| 7 | 0 | High |
| 8 | 0 | High |
| 9 | 0 | High |
| 10 | 0 | High |
| 5 | 2 | Low |
| 6 | 0 | Low |
| 7 | 0 | Low |
| 8 | 0 | Low |
| 9 | 0 | Low |
| 10 | 0 | Low |
| 5 | 1 | Medium |
| 6 | 1 | Medium |
| 7 | 0 | Medium |
| 8 | 0 | Medium |
| 9 | 0 | Medium |
| 10 | 0 | Medium |
How is this calculated?
For Month May 2013 (05-2013), there are two Apps from table Apps
In table Hosts , these apps are associated with device_id's 1,1,1,4,3
For this month (05-2013) for device_id=1, the latest value of start_time is: 2013-05-12 06:22:45 (from tables hosts,apps), so in table Usage, look for combination of appid=2&hostid=2 for which there are two rows one with factor Low and other Medium,
For this month (05-2013) for device_id=4, by following same procedure we get one entry i.e 0 Low
Similarly all the values are calculated.
To get the last 6 months via query i'm trying to get it with the following:
SELECT MONTH(DATE_ADD(NOW(), INTERVAL aInt MONTH)) AS aMonth
FROM
(
SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5
)
Please check sqlfiddle: http://sqlfiddle.com/#!2/55fc2
Because the calculation you're doing involves the same join multiple times, I started by creating a view.
CREATE VIEW `app_host_usage`
AS
SELECT a.id "appid", h.id "hostid", u.id "usageid",
a.userid, a.start_time, h.device_id, u.factor
FROM apps a
LEFT OUTER JOIN hosts h ON h.appid = a.id
LEFT OUTER JOIN `usage` u ON u.appid = a.id AND u.hostid = h.id
WHERE a.start_time > DATE_ADD(NOW(), INTERVAL -7 MONTH)
The WHERE condition is there because I made the assumption that you don't want July 2005 and July 2006 to be grouped together in the same count.
With that view in place, the query becomes
SELECT months.Month, COUNT(DISTINCT device_id), factors.factor
FROM
(
-- Get the last six months
SELECT (MONTH(NOW()) + aInt + 11) % 12 + 1 "Month" FROM
(SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5) LastSix
) months
JOIN
(
-- Get all known factors
SELECT DISTINCT factor FROM `usage`
) factors
LEFT OUTER JOIN
(
-- Get factors for each device...
SELECT
MONTH(start_time) "Month",
device_id,
factor
FROM app_host_usage a
WHERE userid=13
AND start_time IN (
-- ...where the corresponding usage row is connected
-- to an app row with the highest start time of the
-- month for that device.
SELECT MAX(start_time)
FROM app_host_usage a2
WHERE a2.device_id = a.device_id
GROUP BY MONTH(start_time)
)
GROUP BY MONTH(start_time), device_id, factor
) usageids ON usageids.Month = months.Month
AND usageids.factor = factors.factor
GROUP BY factors.factor, months.Month
ORDER BY factors.factor, months.Month
which is insanely complicated, but I've tried to comment explaining what each part does. See this sqlfiddle: http://sqlfiddle.com/#!2/5c871/1/0

how to approach this in MySql query?

I want to select the data as per condition:I have a table with physician_key and corresponding quality score for a given month. I want to select count of distinct physicians with quality score 1,2.
For a month, there could be more entries for a physician_key and accordingly the quality assigned(on scale 1-7). I want to select only the count of those physicians which have quality (1,2) and if the same physician has quality >2 in given month, I don't want to count that physician.I want the information by product and month
I created an example table, since you didn't provide one:
mysql> select * from sales_mkt_rep_qual;
+-------------------+---------+-------+-------------------+
| GEO_PHYSICIAN_KEY | product | month | SALES_REP_QUALITY |
+-------------------+---------+-------+-------------------+
| 1 | a | 8 | 1 |
| 1 | a | 8 | 2 |
| 1 | a | 8 | 3 |
| 2 | b | 8 | 2 |
| 2 | b | 8 | 1 |
| 2 | b | 9 | 2 |
| 1 | a | 9 | 2 |
| 2 | b | 9 | 3 |
| 3 | a | 9 | 2 |
+-------------------+---------+-------+-------------------+
The query from your comment indeed gives an error:
SELECT COUNT(DISTINCT GEO_PHYSICIAN_KEY) AS encount_1to2,
product,MONTH
FROM sales_mkt_rep_qual
WHERE MAX(SALES_REP_QUALITY) = 2 ;
ERROR 1111 (HY000): Invalid use of group function
If you change that to:
SELECT DISTINCT geo_physician_key AS encount_1to2, product, month
FROM sales_mkt_rep_qual
WHERE (geo_physician_key,month,product)
NOT IN (
SELECT geo_physician_key, month, product
FROM sales_mkt_rep_qual
WHERE sales_rep_quality >2 );
you see the detailed result:
+--------------+---------+-------+
| encount_1to2 | product | month |
+--------------+---------+-------+
| 2 | b | 8 |
| 1 | a | 9 |
| 3 | a | 9 |
+--------------+---------+-------+
No, you can introduce the counting:
SELECT COUNT(distinct geo_physician_key ) AS no_of_physicians,product, month
FROM sales_mkt_rep_qual
WHERE (geo_physician_key,month,product)
NOT IN (
SELECT geo_physician_key, month, product
FROM sales_mkt_rep_qual WHERE sales_rep_quality >2 )
GROUP BY month, product;
+------------------+---------+-------+
| no_of_physicians | product | month |
+------------------+---------+-------+
| 1 | b | 8 |
| 2 | a | 9 |
+------------------+---------+-------+
If that still isn't what you are looking for, give more specific table structure and data example.
Try this:
SELECT count(DISTINCT physician_key)
FROM my_table
WHERE month = desired_month
AND max(quality) = 2
GROUP BY month
Actually I want the data to be like the output below:
+--------------+---------+-------+
| encount_1to2 | product | MONTH |
+--------------+---------+-------+
| 2 | b | 8 |
+--------------+---------+-------+
and for the criteria SALES_REP_QUALITY <= 2, isn't there a possibility that while selecting the distinct geo physician key, it might select out of first 2 considering it matches the criteria? Thats the reason I have used Thanix approach of max function with group by product and month, so that the aggregate function is applied on every product within a month