I want to find the time difference between two function names in my database. the database looks like this:
what I want to do is to find the time difference between two consecutive function names who have the same name. for example the output will be for "getPrice" at row number "2" and row number "3" and then time difference for "getPrice"at row "3" and row "5" and so on for all other times and all other function names. Please help me and thanks a lot!
I tried
SELECT a.lid, a.date, (b.date-a.date) as timeDifference
FROM myTable a
INNER JOIN myTable b ON b.lid = (a.lid+1)
ORDER BY a.lid ASC;
The problem is, it gives time difference for any consecutive function names even if they are not identical!
#tombom
there is a table I use for testing and have different variable names than the example I provided earlier. the table looks like this:
and after applying your code (and of course change the variable names to match with this table) the output looks like this:
as you can see the "getTax" is subtracted from "getPrice" although they are different. how can I solve this problem?? Thanks a lot.
the schema I'm trying to build is:
CREATE TABLE `test` (
`id` INT NOT NULL AUTO_INCREMENT ,
`nserviceName` VARCHAR(45) NULL ,
`functionName` VARCHAR(45) NULL ,
`time` TIMESTAMP NULL ,
`tps` INT NULL ,
`clientID` INT NULL ,
PRIMARY KEY (`id`) );
and the insert is :
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('1', 'X1', 'getPrice', '2013-05-23 00:36:08', '22', '0');
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('2', 'X2', 'getTax', '2013-05-23 00:38:00', '33', '0');
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('3', 'X1', 'getPrice', '2013-05-23 00:35:00', '12', '0');
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('4', 'X1', 'getPrice', '2013-05-23 00:35:00', '11', '0');
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('5', 'X2', 'getTax', '2013-05-23 00:35:00', '88', '0');
INSERT INTO `test` (`id`, `nserviceName`, `functionName`, `time`, `tps`, `clientID`) VALUES ('6', 'X1', 'getPrice', '2013-05-23 00:35:00', '33', '0');
thanks.
#tombom
the operation I want to perform on the table is like the following image:
where I start from the first record X1 getPrice which have no record before it. so no operation is required. then check number two getTax have no getPrice before it which are not identical so again no operation will be performed. then number 3 getPrice have getTax before it so it ignores it and check above getTax to find getPrice here it will do the time difference between getPrice(#3) and getPrice(#1). next getPrice at row 4 will check the rows above it, and it find the one directly above it is getPrice so time difference between getPrice*(#4) and getPrice(#3) will be found. then getTax at row 5 will check the rows above it until it finds a similar functionName (getTax) which is at row #2. then the time difference between getTax at row 5 and getTax at row 2 will be found.
thanks a lot..
Please have a try with this one:
SELECT lid, `date`, serviceName, functionName, responseTime, sid, timeDifference FROM (
SELECT
IF(#prevFname = functionName, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, `date`, #prevDate)), 'functionName differs') AS timeDifference,
#prevFname := functionName AS a,
#prevDate := `date` AS b,
yt.*
FROM
yourTable yt
, (SELECT #prevFname:=NULL, #prevDate:=NULL) vars
ORDER BY functionName, `date`
) subquery_alias
I like to use user defined variables in such cases as I made amazing experiences regarding performance, since no self-join is needed.
Also note that I used the timestampdiff function and sec_to_time to polish the output. Timestampdiff is the correct way to subtract different dates(+times). Only downside is, that sec_to_time only allows a range from '00:00:00' to '23:59:59'. If this can lead to problems, remove the function again. Read more about both functions on this site.
UPDATE (less complicated than necessary):
SELECT lid, `date`, serviceName, functionName, responseTime, sid, timeDifference FROM (
SELECT
SEC_TO_TIME(TIMESTAMPDIFF(SECOND, #prevDate, `date`)) AS timeDifference,
#prevDate := `date` AS b,
yt.*
FROM
yourTable yt
, (SELECT #prevDate:=NULL) vars
ORDER BY lid
) subquery_alias
UPDATE 2:
This one resets the timedifference to 00:00:00 when functionName differs to previous one.
SELECT * /*choose here only the columns you need*/ FROM (
SELECT
IF(#prevFunction = functionName, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, #prevDate, `time`)), '00:00:00') AS timeDifference,
#prevFunction := functionName AS a,
#prevDate := `time` AS b,
yt.*
FROM
test yt
, (SELECT #prevDate:=NULL, #prevFunction:=NULL) vars
ORDER BY id
) subquery_alias
UPDATE 3:
Okay, what a difficult birth. Just a minor tweak.
SELECT * /*choose here only the columns you need*/ FROM (
SELECT
IF(#prevFunction = functionName, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, #prevDate, `time`)), '00:00:00') AS timeDifference,
#prevFunction := functionName AS a,
#prevDate := `time` AS b,
yt.*
FROM
test yt
, (SELECT #prevDate:=NULL, #prevFunction:=NULL) vars
ORDER BY functionName, id#, `time`
) subquery_alias
ORDER BY id
I order by function name and id again (or time if you prefer) in the subquery, do all the calculations, then sort it by id again in the outer query. That's it.
Related
My general question seems to be answered by https://stackoverflow.com/a/3025332/3650835, but I do not fully understand it after reading the MySql docs and am wondering if my solution will work as I expect, and also wonder if LIMIT 1 is needed.
Goal: to ensure that, for a given user_id, start and end never "overlap". As an example:
test_table
user_id start end
4 1 5
4 6 13
4 11 17 --> NOT allowed, bc 11 <= 13
2 1 9 --> allowed, user_id is different
My current solution
/* this should not insert anything, as it would cause an "overlap" of start
and end, based on row 2 having end = 13 */
INSERT INTO `test_table` (user_id, start, end)
SELECT '4', '11', '17' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '4' AND end >= '11')
LIMIT 1;
Does the WHERE NOT EXISTS section mean "only do this insert...if this following select returns nothing"?
Also, there was the following comment in the linked solution, but I do not understand why based on the MySql docs this would be true. If true, I could remove Limit 1 from my solution:
If you use "from dual" on line 2 instead of "from table", then you
don't need the "limit 1" clause
Thanks for your time.
Edit: here is all the sql for testing/setup:
CREATE TABLE `test_table`(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT,
`start` INT,
`end` INT
);
INSERT INTO `test_table` (user_id, start, end)
SELECT '4', '1', '5' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '4' AND end >= '1')
LIMIT 1;
INSERT INTO `test_table` (user_id, start, end)
SELECT '2', '1', '9' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '2' AND end >= '1')
LIMIT 1;
INSERT INTO `test_table` (user_id, start, end)
SELECT '4', '6', '13' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '4' AND end >= '6')
LIMIT 1;
/* this should not insert anything, as it would cause an "overlap" of start and end */
INSERT INTO `test_table` (user_id, start, end)
SELECT '4', '11', '17' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '4' AND end >= '11')
LIMIT 1;
I'd use triggers instead:
-- This is your original table create statement
DROP TABLE IF EXISTS `test_table`;
CREATE TABLE `test_table`(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT,
`start` INT,
`end` INT
);
DELIMITER //
CREATE TRIGGER `TRG_test_table_before_insert` BEFORE INSERT ON `test_table` FOR EACH ROW BEGIN
SELECT
COUNT(*) INTO #cnt
FROM
test_table
WHERE
`user_id` = NEW.user_id
AND `start` <= NEW.`end`
AND `end` >= NEW.`start`
;
IF(#cnt > 0) THEN
SET #msg = CONCAT('TrgErr: overlapping, user_id = ', NEW.user_id, ', start = ', NEW.`start`, ', end = ', NEW.`end`);
SIGNAL SQLSTATE '45000' SET message_text = #msg;
END IF;
END//
DELIMITER ;
Then you will be able to use normal insert statements:
INSERT INTO `test_table` (user_id, `start`, `end`) VALUES ('4', '1', '5');
INSERT INTO `test_table` (user_id, `start`, `end`) VALUES ('2', '1', '9');
INSERT INTO `test_table` (user_id, `start`, `end`) VALUES ('4', '6', '13');
INSERT INTO `test_table` (user_id, `start`, `end`) VALUES ('4', '11', '17'); -- this one will not be inserted
In addition, you can implement the similar settings for updates.
P.S. You should check overlapping logic in my code as I do not know if start = end should be allowed or not.
P.P.S. Index (user_id, begin) will also help
NOT EXISTS means that get results for the inner query and if it returns any rows dont include that related row in the main query. But, your subquery does not have any relation with your main query, which makes me to think that the query may not be generating correct results.
select * from tbl1
where not exists (
select 1 from tbl2 where tbl1.id = tbl2.id
)
the above query makes lot more sense. It means that for every record in tbl1 check tbl2 and if any result is found dont include it in the query result.
It might be easier to understand what your query is doing if it is rewritten like this:
INSERT INTO `test_table` (user_id, start, end)
SELECT user_id, start, end
FROM ( SELECT 4 AS `user_id`, 6 AS `start`, 13 AS `end`) AS candidate
WHERE NOT EXISTS (
SELECT *
FROM `test_table` AS t
WHERE t.user_id = candidate.user_id AND t.end >= candidate.`end`
)
;
Also, note I removed the single quotes around the numbers; it may or may not be a problem in this case, but in some scenarios that could have resulted in some hard to find bugs where 2 > 11 (if MySQL decided to cast the t.end to a char type to compare with candidate.end).
A select from DUAL will only return ever return one row, so the LIMIT 1 is not required. However if you use your table name, your query will return either as many rows as are in the table, or none, dependent on whether the EXISTS expression returns true or false. So in that case you will need the LIMIT 1.
Your interpretation of what the WHERE NOT EXISTS does is correct.
If (start,end) pairs are only ever inserted in order, your existing test on end is sufficient. If however they might go backwards e.g. (4, 1, 2), (4, 5, 6), (4, 3, 4), then you should change your WHERE clause in the subquery to also test the start value, e.g. the last query should be written as
INSERT INTO `test_table` (user_id, start, end)
SELECT '4', '11', '17' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `test_table`
WHERE user_id = '4' AND
(start <= 11 AND end >= 11 OR start <= 17 AND end >= 17))
I made a small demo on dbfiddle to show how these work.
I am trying to count the entries for the current day and sum the total. Currently, I have a query that counts the entries per day. I am using the datetime field to achieve my end goal. What would be the best approach to count the entries for the current day and sum the total?
CREATE TABLE product_entry
(`id` int, `entry_time` datetime, `product` varchar(55))
;
INSERT INTO product_entry
(`id`, `entry_time`, `product`)
VALUES
(1, '2015-09-03 15:16:52', 'dud1'),
(2, '2015-09-03 15:25:00', 'dud2'),
(3, '2015-09-04 16:00:12', 'dud3'),
(4, '2015-09-04 17:23:29', 'dud4')
;
SQLFIDDLE
Query
SELECT entry_time, count(*)
FROM product_entry
GROUP BY hour( entry_time ) , day( entry_time )
Schema
CREATE TABLE product_entry
(`id` int, `entry_time` datetime, `product` varchar(55))
;
INSERT INTO product_entry
(`id`, `entry_time`, `product`)
VALUES
(1, '2015-09-03 15:16:52', 'dud1'),
(2, '2015-09-03 15:25:00', 'dud2'),
(3, '2015-09-04 16:00:12', 'dud3'),
(4, '2015-09-04 17:23:29', 'dud4')
;
The title of your question says Count results for the current date ..., but the query you have tried suggests you want to show result counts for every distinct date. I am not sure which one you need. If the former is the case, you could simply use:
SELECT COUNT(`id`) FROM `product_entry` WHERE DATE(`entry_time`) = CURDATE()
To get count for today:
SELECT COUNT(`id`) FROM `product_entry` WHERE DATE(`entry_time`) = CURRENT_DATE
To get count for yesterday (needed when You want to get entries at end of the day):
SELECT COUNT(`id`) FROM `product_entry` WHERE DATE(`entry_time`) = SUBDATE(CURRENT_DATE, 1)
For all time grouped by date and formated:
SELECT DATE_FORMAT(entry_time,'%Y-%m-%d'), count(*)
FROM product_entry
GROUP BY date(entry_time)
this is MSSQL Code maybe your help
SELECT day([product_entry].[entry_time])as input, count(*) as Miktar
FROM [product_entry]
GROUP BY day([entry_time])
i am looking how to have unique value per day, here an exemple:
CREATE TABLE IF NOT EXISTS calls (
id int(11) default NULL,
calldate datetime default NULL,
dst varchar(80) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO calls (id, calldate, dst) VALUES
(1, '2014-05-03 20:45:43', '22561037352'),
(2, '2014-05-04 20:07:49', '22561037352'),
(3, '2014-05-04 13:16:14', '22561037352'),
(4, '2014-05-04 20:08:58', '22560991034'),
(5, '2014-05-04 16:06:02', '22560991034'),
(6, '2014-05-04 20:22:19', '22560842218');
sqlfiddle: Copy and paste to test
we have two number coming two times per day 22561037352 & 22560991034, i want to show unique number per day.
http://sqlfiddle.com/#!9/84076/11
SELECT DISTINCT DATE_FORMAT (calldate,'%Y-%m-%d') calldate, dst
FROM calls
SELECT DISTINCT t.*
FROM (SELECT DATE (calldate) as calldate, dst FROM calls) t
First call to the number for the day
SELECT *
FROM calls c
WHERE NOT EXISTS (SELECT 'a'
FROM calls c2
WHERE c2.dst = c.dst
AND DATE_FORMAT(c2.calldate,'%d/%m/%Y') = DATE_FORMAT(c.calldate,'%d/%m/%Y')
AND c2.calldate < c.calldate
)
What I want to achieve is,
If the number of rows returned by the query:
SELECT count(*) FROM `games` WHERE cateID=2 AND active=1
is EQUAL to 0, I want to run an inserting query:
INSERT INTO games(cateID ,time,users,active)
VALUES ('2', '1', '0', '1')
I tried using case like this:
SELECT CASE WHEN SELECT count(*) FROM `games` WHERE cateID=2 AND active=1)=0
THEN INSERT INTO games(cateID ,time,users,active)
VALUES ('2', '1', '0', '1')
END
But it returns error as:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT count(*) FROM `games` WHERE cateID=2 AND active=1)=0
THEN INSERT INTO' at line 1
Note: Both queries when executed separately, execute without any error.
How do I achieve this ?
EDIT:
Also tried this with IF,
SELECT if(count(*)==0,INSERT INTO games(cateID ,time,users,active)
VALUES ('2', '1', '0', '1')) FROM `games` WHERE cateID=2 AND active=1
Still gives the same error.
EDIT 2:
By the suggested comment,
INSERT INTO games(cateID ,time,users,active)
select '2','2','0','1'
where (SELECT count(*) FROM `games` WHERE cateID=2 AND active=1) <= 0
Still gives error.
Try this
DECLARE
games_count INTEGER;
BEGIN
SELECT count(*) FROM `games` WHERE cateID=2 AND active=1 ;
IF games_count = 0 THEN
INSERT INTO games(cateID ,time,users,active) VALUES ('2', '1', '0', '1');
END IF;
END;
before to do the insert, check the select query. if the select query return 0 records nothing is inserting with the insert query.
select cateID,'2' as time,'0' as users,active
FROM
(select count(*) as countRecord,cateID,active
from games
where CATEID=2 and active=1 group by cateID,active) G
where countRecord <= 0
group by cateID, active,time,users
I am working on a scraping project to crawl items and their scores over different schedules.Schedule is a user defined period (date) when the script is intended to run.
Table structure is as follows:
--
-- Table structure for table `test_join`
--
CREATE TABLE IF NOT EXISTS `test_join` (
`schedule_id` int(11) NOT NULL,
`player_name` varchar(50) NOT NULL,
`type` enum('celebrity','sportsperson') NOT NULL,
`score` int(11) NOT NULL,
PRIMARY KEY (`schedule_id`,`player_name`,`type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `test_join`
--
INSERT INTO `test_join` (`schedule_id`, `player_name`, `type`, `score`) VALUES
(1, 'sachin', 'sportsperson', 100),
(1, 'ganguly', 'sportsperson', 80),
(1, 'dravid', 'sportsperson', 60),
(1, 'sachin', 'celebrity', 100),
(2, 'sachin', 'sportsperson', 120),
(2, 'ganguly', 'sportsperson', 100),
(2, 'sachin', 'celebrity', 120);
The scraping is done over periods and for each schedule it is expected to have about 10k+ entries.The schedules could be made in daily basis,hence the data would grow to be be around 2 million in 5-6 months.
Over this data I need to perform queries to aggregate the player who come across each schedules in a selected range of schedules.
For example:
I need aggregate same players who come across multiple schedules. If schedule 1 and 2 are selected,items which come under both of the schedules only will be selected.
I am using the following query to aggregate results based on the type,
For schedule 1:
SELECT fullt.type,COUNT(*) as count,SUM(fullt.score) FROM
(SELECT tj.*
FROM `test_join` tj
RIGHT JOIN
(SELECT `player_name`,`type`,COUNT(`schedule_id`) as c FROM `test_join` WHERE `schedule_id` IN (1,2) GROUP BY `player_name`,`type` HAVING c=2) stj
on tj.player_name = stj.player_name
WHERE tj.`schedule_id`=1
GROUP BY tj.`type`,tj.`player_name`)AS fullt
GROUP BY fullt.type
Reason for c = 2;
WHERE `schedule_id` IN (1,2) GROUP BY `player_name`,`type` HAVING c=2
Here we are selecting two schedules,1 and 2.Hence the count 2 is taken to make the query to to fetch records which belongs to both the schedules and occurs twice.
It would generate a results as follows,
Schedule 1 :Expected Results
Schedule 2 :Expected Results
This is my expected result and the query returns the results as above.
(In the real case I have to work across pretty big MySQL tables)
On my understanding of standardized MySQL queries, using sub queries,WHERE IN, varchar comparison fields ,multiple GROUP BY's would affect in the query performance.
I need the aggregate results in real time and query speed and well as standards are a concern too.How this could be optimized for better performance in this context.
EDIT:
I had reduced sub queries now:
SELECT fullt.type,COUNT(*) as count,SUM(fullt.score) FROM (
SELECT t.*
FROM `test_join` t
INNER JOIN test_join t1 ON t.`player_name` = t1.player_name AND t1.schedule_id = 1
INNER JOIN test_join t2 ON t.player_name = t2.player_name AND t2.schedule_id = 2
WHERE t.schedule_id = 2
GROUP BY t.`player_name`,t.`type`) AS fullt
GROUP BY fullt.type
Is this a better way to do so.I had replaced WHERE IN with JOINS.
Any advise would be highly appreciated.I would be happy to provide any supporting information if needed.
try below SQL Query in MYSQL:
SELECT tj.`type`,COUNT(*) as count,SUM(tj.`score`) FROM
`test_join` tj
where tj.`schedule_id`=1
and `player_name` in
(
select tj1.`player_name` from `test_join` tj1
group by tj1.`player_name` having count(tj1.`player_name`) > 1
)
group by tj.`type`
Actuallly I tried same data in Sybase as i dont have MySQL installed in my machine.It worked as exepected !
CREATE TABLE #test_join
(
schedule_id int NOT NULL,
player_name varchar(50) NOT NULL,
type1 varchar(15) NOT NULL,
score int NOT NULL,
)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES
(1, 'sachin', 'sportsperson', 100)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'ganguly', 'sportsperson', 80)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'dravid', 'sportsperson', 60)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'sachin', 'celebrity', 100)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'sachin', 'sportsperson', 120)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'ganguly', 'sportsperson', 100)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'sachin', 'celebrity', 120)
select * from #test_join
Print 'Solution #1 : Inner join'
select type1,count(*),sum(score) from
#test_join
where schedule_id=1 and player_name in (select player_name from #test_join t1 group by player_name having count(player_name) > 1 )
group by type1
select player_name,type1,sum(score) Score into #test_join_temp
from #test_join
group by player_name,type1
having count(player_name) > 1
Print 'Solution #2 using Temp Table'
--select * from #test_join_temp
select type1,count(*),sum(score) from
#test_join
where schedule_id=1 and player_name in (select player_name from #test_join_temp )
group by type1
I hope This Helps :)