Query of retrieving unique users who overwrite each other - mysql

I have the following set of data :
id changeType dateTimeStamp userName
1 insert 07/02/2015 0:15:53 john
2 update 07/02/2015 0:15:52 adriana
3 insert 07/02/2015 0:15:51 john
4 update 07/02/2015 0:15:50 john
5 update 07/01/2015 22:46:57 denise
I am trying to retrieve users who overwrites each other's data that made a change within 5 minutes of each other. For this particular sets of data, I should get IDs 1, 2, and 3.
I tried :
SELECT *
FROM tblChanges v1
INNER JOIN tblChanges v2
ON v2.userName <> v1.userName
AND v1.dateTimeStamp <> v2.dateTimeStamp
WHERE ABS( TIMESTAMPDIFF(MINUTE, v1.dateTimeStamp, v2.dateTimeStamp ) ) <= 5
This also retrieves ID=4 which I don't need. Any help?
thanks in advance.
EDIT : Output would be
id changeType dateTimeStamp userName
1 insert 07/02/2015 0:15:53 john
2 update 07/02/2015 0:15:52 adriana
3 insert 07/02/2015 0:15:51 john
EDIT: here are the DDL / DML
CREATE TABLE `tblChanges` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`changeType` VARCHAR(50) DEFAULT NULL,
`dateTimeStamp` DATETIME NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`userName` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (1, 'insert' , '2015-07-02 12:15:53', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (2, 'update' , '2015-07-02 12:15:52', 'adriana' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (3, 'insert' , '2015-07-02 12:15:51', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (4, 'update' , '2015-07-02 12:15:50', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (5, 'update' , '2015-07-01 22:46:57', 'denise' ) ;

Your join is selecting records that occur both before and after the current record, instead of only after.
Suggest:
SELECT *
FROM tblChanges v1
INNER JOIN tblChanges v2
ON v2.userName <> v1.userName
AND v2.dateTimeStamp BETWEEN v1.dateTimeStamp AND DATE_ADD(v1.dateTimeStamp, INTERVAL 5 MINUTE)

Related

Find and Replace using Update Statement via Inner Join in MySQL

I'm having two tables namely ds_message and ds_params, the first table contains the template and the second tables contains the key value pair
Table Structure: ds_message
_____________________________________________
id template
_____________________________________________
1 'Dear {a}, the price of {b} is {c}'
2 'Dear {i}, you selected the product {j}'
Schema:
CREATE TABLE `ds_message` (
`id` int NOT NULL,
`template` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='';
ALTER TABLE `ds_message`
ADD PRIMARY KEY (`id`);
INSERT INTO `ds_message` (`id`, `template`) VALUES
(1, 'Dear {a}, the price of {b} is {c}');
INSERT INTO `ds_message` (`id`, `template`) VALUES
(2, 'Dear {i}, you selected the product {j}');
Table Structure: ds_params
_________________________________________________
id message_id json_key json_value
_________________________________________________
1 1 a John
2 1 b bat
3 1 c $10
4 2 i Emma
5 2 j Jam
Schema:
CREATE TABLE `ds_params` (
`id` int NOT NULL,
`message_id` int NOT NULL,
`json_key` varchar(500) NOT NULL,
`json_value` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='';
ALTER TABLE `ds_params`
ADD PRIMARY KEY (`id`);
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(1, 1, 'a', 'John');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(2, 1, 'b', 'bat');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(3, 1, 'c', '$10');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(4, 2, 'i', 'Emma');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(5, 2, 'j', 'Jam');
I need to replace the keys (for example {a} => John) in the ds_message table.
I tried the following code,
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value);
Once I executed the code I'm getting the output like this, only the first key gets replaced, remaining keys not get update.
_____________________________________________
id template
_____________________________________________
1 Dear John, the price of {b} is {c}
2 Dear Emma, you selected the product {j}
Kindly assist me how to do this.
We have 2 options.
First is using while loop.
SET #n = 0;
SELECT COUNT(*) FROM ds_params INTO #n;
SET #i=0;
WHILE #i<#n DO
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id AND m.id = #i
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value)
SET #i = #i + 1;
END WHILE;
2nd Option is this. If you have a fix json_key
UPDATE ds_message AS t
LEFT JOIN ds_params a ON a.message_id = t.id and a.json_key='a'
LEFT JOIN ds_params b ON b.message_id = t.id and b.json_key='b'
LEFT JOIN ds_params c ON c.message_id = t.id and c.json_key='c'
LEFT JOIN ds_params i ON i.message_id = t.id and i.json_key='i'
LEFT JOIN ds_params j ON j.message_id = t.id and j.json_key='j'
SET t.template = REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(t.template, CONCAT('{', IFNULL(a.json_key, '') , '}')
, IFNULL(a.json_value, '')),
CONCAT('{', IFNULL(b.json_key, '') , '}'), IFNULL(b.json_value, '')),
CONCAT('{', IFNULL(c.json_key, '') , '}'), IFNULL(c.json_value, '')),
CONCAT('{', IFNULL(i.json_key, '') , '}') ,IFNULL(i.json_value, '')),
CONCAT('{', IFNULL(j.json_key, '') , '}') ,IFNULL(j.json_value, ''));
You need to perform loop in this update. Since there is no fix key value pair so it is better to find the max key value pair to perform loop.
After that loop through each record using CURSOR to perform the update to replace your key with corresponding value. Since you already find out the max value of key pair so it will perform only till the max limit of loop for your query.
For reference you may find this link for details on CURSOR.
DECLARE a, b VARCHAR(10);
DECLARE cur1 CURSOR FOR SELECT DISTINCT json_key,json_value
FROM ds_params.t1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO a, b;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value)
WHERE M.JSON_KEY = b ;
END LOOP;
CLOSE cur1;
I am not sure about syntax but you may get the idea of implementing the same. If anything in syntax is not correct then kindly update the same.

How to sort by timestamp streaks SQL

Say I have the following tables (simplified version of what I'm working with):
CREATE TABLE posts (
id INTEGER,
title VARCHAR(255),
text TEXT,
author_id INTEGER,
created_at TIMESTAMP
);
CREATE TABLE authors (
id INTEGER,
name VARCHAR(255),
email VARCHAR(255)
);
What I want to do is retrieve only the authors but order them by number of one-week streaks. That is, number of consecutive weeks an author published a post. The time a post has been made is stored in the posts created_at column
What I'm having the most difficulty with is understanding how to calculate the difference in time between posts across rows. I’m using MySQL
Without window functions it's a bit hard to do in MySql 5.7
But here's an experiment test snippet that uses variables :
Sample data:
DROP TABLE IF EXISTS `posts`;
DROP TABLE IF EXISTS `authors`;
CREATE TABLE `authors` (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
email VARCHAR(255)
);
CREATE TABLE `posts` (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255),
`text` TEXT,
author_id INTEGER,
created_at TIMESTAMP,
CONSTRAINT fk_posts_author_id FOREIGN KEY (author_id) REFERENCES `authors`(id)
);
insert into `authors` (name, email) values
('john doe', 'john.doe#home.net'),
('jane sheppard', 'jane.sheppard#home.net');
insert into `posts` (author_id, created_at, title, `text`) values
(1, '2019-02-07', 'When', 'bla'),
(1, '2019-02-09', 'I', 'bla2'),
(1, '2019-02-14', 'Start', 'bla3'),
(1, '2019-02-19', 'looking', 'bla4'),
(1, '2019-03-10', '...', 'bla5'),
(2, '2019-02-01', 'I', 'blah1'),
(2, '2019-02-05', 'frighten', 'blah2'),
(2, '2019-02-19', 'even', 'blah3'),
(2, '2019-03-20', 'myself', 'blah4');
Query:
SELECT q3.ConcurrentWeeks, q3.StartWeekDate, a.*
FROM
(
SELECT COUNT(*) as ConcurrentWeeks, MIN(WkDt) as StartWeekDate, author_id
FROM
(
SELECT q1.WkDt, q1.Total
, case
when #author = author_id and #yr = yr and #wk = wk-1 then #rnk
else #rnk := #rnk + 1
end as rnk
, #author := author_id as author_id
, #yr := yr as yr
, #wk := wk as wk
FROM
(
SELECT
author_id, YEAR(created_at) as yr, WEEK(created_at) as wk
, COUNT(*) AS Total
, COALESCE(MIN(STR_TO_DATE(concat(YEAR(created_at),' monday ',WEEK(created_at)),'%X %W %V')), MIN(CAST(created_at AS DATE))) AS WkDt
FROM `posts` p
GROUP BY author_id, YEAR(created_at), WEEK(created_at)
ORDER BY author_id, yr, wk
) q1
CROSS JOIN (select #author := null, #yr := null, #wk := null, #rnk := 0) init
) q2
GROUP BY author_id, rnk
HAVING ConcurrentWeeks > 1
) q3
LEFT JOIN `authors` a ON a.id = q3.author_id
ORDER BY ConcurrentWeeks DESC, StartWeekDate ASC
Result:
ConcurrentWeeks StartWeekDate id name email
--------------- ------------- -- ------------- ----------------------
3 2019-02-04 1 john doe john.doe#home.net
2 2019-01-28 2 jane sheppard jane.sheppard#home.net

mysql, query how to goup same value and count that value

i have data table like that :
month IKU value
2 1b 1
2 1b 1
1 2a 1
1 1a 1
the results I want like that :
month IKU value
2 1b 2
1 1a,2a 1,1
is that possible?
You need to use GROUP BY twice along with GROUP_CONCAT function like below:
SELECT
tmp.month,
GROUP_CONCAT(tmp.IKU) AS IKU,
GROUP_CONCAT(tmp.summedValue) AS value
FROM
(
SELECT
month,
IKU,
SUM(value) AS summedValue
FROM t
GROUP BY month,IKU
) AS tmp
GROUP BY tmp.month
ORDER BY tmp.month DESC
GROUP_CONCAT() function
MySQL GROUP_CONCAT() function returns a string with concatenated non-NULL value from a group.
Returns NULL when there are no non-NULL values.
Test:
-- ----------------------------
-- Table structure for `t`
-- ----------------------------
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t` (
`month` int(11) DEFAULT NULL,
`IKU` varchar(10) DEFAULT NULL,
`value` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of t
-- ----------------------------
INSERT INTO `t` VALUES ('2', '1b', '1');
INSERT INTO `t` VALUES ('2', '1b', '1');
INSERT INTO `t` VALUES ('1', '2a', '1');
INSERT INTO `t` VALUES ('1', '1a', '1');
Try this. Didn't get chance to test it as MySQL fiddle isn't working, but the idea is to first sum(value) for each combination of month,iku. Then use group_concat to concatenate the resultset.
select month
,group_concat(IKU separator ',')
,group_concat(value separator ',')
from
(select month,IKU,sum(value) as value
from Table1 group by month,IKU
) t
group by IKU

MySQL wrong result using MIN() function

Question:
In this example, we have the grades of 5 students from school 1. We want to know which student had the lowest grade.
We were expecting to get student number 4, but SQL returns student 1
Can someone help me?
Thanks in advance
Table 1:
CREATE TABLE `table1` (
`school_id` int(11) unsigned NOT NULL,
`student_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`grade` int(11) unsigned NOT NULL,
PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
Data:
INSERT INTO `table1` (`school_id`, `student_id`, `grade`)
VALUES
(1, 1, 20),
(1, 2, 15),
(1, 3, 18),
(1, 4, 12),
(1, 5, 15);
SQL Query:
SELECT t1.`school_id`, t1.`student_id`, MIN(t1.grade)
FROM table1 as t1
WHERE t1.`school_id`=1
GROUP BY t1.`school_id`;
Printscreen:
SELECT * FROM table1 ORDER BY grade LIMIT 1
If you want the worst performing student in each school, then that's...
SELECT x.*
FROM table1 x
JOIN
( SELECT school_id
, MIN(grade) grade
FROM table1
GROUP
BY school_id
) y
ON y.school_id = x.school_id
AND y.grade = x.grade;
http://sqlfiddle.com/#!9/f44cb2/1
With #tadman's tip, we came up with a solution:
You can find it bellow in case you came across with this same issue.
We didn't understand why we have to use the limit. if we take out the limit line, we will get a wrong result
SELECT t2.`school_id`, t2.`student_id`, t2.grade
FROM
(
SELECT t1.`school_id`, t1.`student_id`, t1.grade
FROM table1 as t1
WHERE t1.`school_id`=1
ORDER BY t1.`grade` ASC
limit 4294967295
)
as t2
GROUP BY t2.`school_id`;
Unless there are some more requirements for your problem, I guess you would be good with just:
select t1.school_id, t1.student_id, t1.grade
from table1 as t1,
(select school_id, min(grade) as grade from table1 group by school_id) as t2
where t1.school_id=t2.school_id
and t1.grade=t2.grade;

MySQL - Select multiple values across multiple columns where all matches have a result

I am trying to construct a query that will allow me to "filter" on pairs of columns for particular criteria. I need to be able to construct multiple filters for the same given pair. The end result should only return instances that have data for the case where all filters are applied.
I constructed a trivial example demonstrating what I would like to be able to do.
Using the follow table definition:
DROP TABLE IF EXISTS foo;
CREATE TEMPORARY TABLE `foo` (
`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`Index` INT UNSIGNED NOT NULL,
`Header` VARCHAR(50) NOT NULL,
`Value` VARCHAR(255) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE INDEX `ID_UNIQUE` (`ID` ASC));
INSERT INTO `foo` (`Index`, `Header`, `Value`)
VALUES
(0, 'Header_1', 'a'),
(0, 'Header_2', 'b'),
(1, 'Header_1', 'a'),
(1, 'Header_2', 'c');
I would like a query that would return the following, given that you are looking for the case where 'Header_1' == 'a' and 'header_2' == 'b':
Index | Header | Value
------------------------
0 | Header_1 | a
0 | Header_2 | b
My current attempt is as follows:
SELECT `Index`, `Header`, `Value` FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Header`, `Value`
HAVING COUNT(DISTINCT `Index`) = 2
ORDER BY `Index`, `Header`;
That code returns the following:
Index | Header | Value
------------------------
0 | Header_1 | a
I am missing one of my return rows. How can I restructure the query to return all of the matching rows?
Note that I declared the table as a temporary table. This is important, as I am working with temporary tables, and they have special restrictions to keep in mind (namely not being able to open it more than once in the same statement).
Your query returns only header_1 because the clause:
HAVING COUNT(DISTINCT `Index`) = 2
is only correct for Header_1.
Header_2 has count=1, therefore removed from the end result.
To get a clearer picture of what i say use:
SELECT `Index`, `Header`, `Value`, COUNT(DISTINCT `Index`) FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Header`, `Value`
ORDER BY `Index`, `Header`;
and take a look at the last column.
I couldn't figure out how to do this with only the one temporary table. I'm not happy with this result, but at least it works.
DROP TABLE IF EXISTS `foo2`;
CREATE TEMPORARY TABLE `foo2` (
SELECT `Index` FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Index`
HAVING COUNT(DISTINCT `Header`) = 2
);
SELECT DISTINCT t1.`Index`, t1.`Header`, t1.`Value` FROM `foo` t1
INNER JOIN `foo2` t2 ON t2.`Index` = t1.`Index`
ORDER BY t1.`Index`, t1.`Header`;
How about...
SELECT `index`
FROM foo
WHERE (header,value) IN (('header_1','a'))
OR (header,value) IN (('header_2','b'))
GROUP
BY `index`
HAVING COUNT(*) = 2;