mysql 5.6: group_concat help sought - mysql

I have the tables:
CREATE TABLE IF NOT EXISTS `buildingAccess` (
`id` int(10) unsigned NOT NULL,
`building` varchar(16) NOT NULL,
`person` varchar(16) NOT NULL,
`enteryDate` datetime NOT NULL,
`exitDate` datetime DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
INSERT INTO `buildingAccess` (`id`, `building`, `person`, `enteryDate`, `exitDate`) VALUES
(1, 'Lot B-3', 'Alice Jones', '2015-11-10 05:29:14', '2015-11-10 15:18:42'),
(3, 'Lot B-3', 'Alice Jones', '2015-11-11 07:11:27', '2015-11-11 12:43:34'),
(7, 'Lot B-3', 'Alice Jones', '2015-12-10 07:11:27', '2015-12-11 12:43:34'),
(2, 'Lot B-3', 'Bill Mayhew', '2015-11-10 10:29:14', '2015-11-10 12:18:42'),
(4, 'Lot B-3', 'Bill Mayhew', '2015-11-12 09:10:27', '2015-11-13 02:43:34'),
(8, 'Lot B-3', 'Bill Mayhew', '2015-11-12 09:10:27', '2015-11-13 02:43:34'),
(5, 'Lot B-3', 'Charlotte Ahn', '2015-12-01 05:29:14', NULL),
(6, 'Lot B-3', 'Dennis Lowe', '2015-12-10 10:29:14', '2015-12-10 12:18:42');
CREATE TABLE IF NOT EXISTS `buildingNotes` (
`building` varchar(16) NOT NULL,
`observer` varchar(16) NOT NULL,
`observationDate` datetime NOT NULL,
`note` varchar(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `buildingNotes` (`building`, `observer`, `observationDate`, `note`) VALUES
('Lot B-3', 'Alice Jones', '2015-11-10 05:32:12', 'burned out light on pole South-22'),
('Lot B-3', 'Alice Jones', '2015-11-10 05:39:12', 'burned out light on pole West-7'),
('Lot B-3', 'Alice Jones', '2015-11-10 05:42:12', 'overfull trash can near pole North-11'),
('Lot B-3', 'Charlotte Ahn', '2015-12-01 06:09:14', 'change drawr running low at gate 3');
ALTER TABLE `buildingAccess`
ADD PRIMARY KEY (`id`), ADD KEY `building` (`building`,`person`,`enteryDate`,`exitDate`);
ALTER TABLE `buildingNotes`
ADD KEY `building` (`building`,`observer`,`observationDate`,`note`);
ALTER TABLE `buildingAccess`
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=9;
My goal is a query that returns a list of all records in the buildingAccess table. Each should have a notes field that is the GROUP_CONCAT of all of the buildingNotes.note entries made during that record's dates/times bracketed by buildingAccess.enteryDate and buildingAccess.exitDate.
I have tried a few things but am stuck at:
select
BA.building,
BA.person,
BA.enteryDate,
IFNULL(BA.exitDate, NOW()),
IFNULL(
GROUP_CONCAT(
'<p>',
BN.observationDate, ': ',
BN.observer, ': ', BN.note,
'</p>'
ORDER BY BN.observationDate ASC
SEPARATOR ''
),
''
)
from
buildingAccess BA
LEFT JOIN buildingNotes BN ON
BN.building = BA.building
AND BN.observationDate BETWEEN BA.enteryDate AND BA.exitDate
group by BN.building
This returns:
+----------+---------------+---------------------+---------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| building | person | enteryDate | exitDate | observations |
+----------+---------------+---------------------+---------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Lot B-3 | Charlotte Ahn | 2015-12-01 05:29:14 | 2016-01-23 23:04:04 | |
| Lot B-3 | Alice Jones | 2015-11-10 05:29:14 | 2015-11-10 15:18:42 | <p>2015-11-10 05:32:12: Alice Jones: burned out light on pole South-22</p><p>2015-11-10 05:39:12: Alice Jones: burned out light on pole West-7</p><p>2015-11-10 05:42:12: Alice Jones: overfull trash can near pole North-11</p> |
+----------+---------------+---------------------+---------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
I expected to see all of the other buildingAccess records even if there were no buildingNotes records.
I am assuming that I am not "grouping by" the right things but i have not found the right combination yet.
Pointers?

I believe it is due to use BN.building which is from the outer joined table in the GROUP BY clause. Try the following:
SELECT
BA.building
, BA.person
, BA.enteryDate
, IFNULL(BA.exitDate, NOW())
, IFNULL (
GROUP_CONCAT (
'<p>',
BN.observationDate, ': ',
BN.observer, ': ', BN.note,
'</p>'
ORDER BY BN.observationDate ASC
SEPARATOR ''
)
,''
)
FROM buildingAccess BA
LEFT JOIN buildingNotes BN ON BN.building = BA.building
AND BN.observationDate BETWEEN BA.enteryDate AND BA.exitDate
GROUP BY
BA.building
, BA.person
, BA.enteryDate
, IFNULL(BA.exitDate, NOW())
It s possible this is too many rows, and perhaps you need some other way to handle (for example) only getting the date rather than full datetime. However you should routinely specify ALL the non-aggregating columns of a query in the group by caluse. See MySQL GROUP BY Extension

Related

How can I see the employee IDs for a project that lasts longer than 20 hours per week?

(assigned_name in works_on is percentage of time employee is assigned to project)
Question:
Find the IDs of employees assigned to a project that is more than 20 hours per week.
My attempt:
I have assumed 24 hours as working hours here. Is my query correct?
Here's the create and insert statements.
CREATE TABLE IF NOT EXISTS employees(
employee_id NUMERIC(9),
first_name VARCHAR(10),
last_name VARCHAR(20),
dept_code CHAR(5),
salary NUMERIC(9, 2),
PRIMARY KEY (employee_id)
);
CREATE TABLE IF NOT EXISTS departments(
code CHAR(5),
name VARCHAR(30),
manager_id NUMERIC(9),
sub_dept_of CHAR(5),
PRIMARY KEY (code)
);
CREATE TABLE IF NOT EXISTS projects(
project_id CHAR(8),
dept_code CHAR(5),
description VARCHAR(200),
start_date DATE,
stop_date DATE,
revenue NUMERIC(12, 2),
PRIMARY KEY (project_id)
);
CREATE TABLE IF NOT EXISTS works_on(
employee_id NUMERIC(9),
project_id CHAR(8),
assigned_time NUMERIC(3, 2),
PRIMARY KEY (employee_id, project_id)
);
INSERT INTO employees
VALUES (1, 'Al', 'Betheleader', 'ADMIN', 70000),
(2, 'Pl', 'Rsquared', 'ACCNT', 40000),
(3, 'Harry', 'Hardware', 'HDWRE', 50000),
(4, 'Sussie', 'Software', 'CNSLT', 60000),
(5, 'Abe', 'Advice', 'CNSLT', 30000),
(6, 'Hardly', 'Aware', NULL, 65000),
(7, 'Bucky', 'Nour', 'ACTNG', 25000);
INSERT INTO departments
VALUES ('ADMIN', 'Administration', 1, NULL),
('ACCNT', 'Accounting', 2, 'ADMIN'),
('HDWRE', 'Hardware', 3, 'CNSLT'),
('CNSLT', 'Consulting', 4, 'ADMIN'),
('ACTNG', 'Bug fixing', 7, 'ADMIN');
INSERT INTO projects
VALUES
('EMPHAPPY', 'ADMIN', 'Employee Moral', '2002-03-14', NULL, 0),
('ADT4MFIA', 'ACCNT', 'Mofia Audit', '2003-07-03', '2003-11-30', 100000),
('ROBOSPSE', 'CNSLT', 'Robotic Spouse', '2002-03-14', NULL, 242000),
('DNLDCLNT', 'CNSLT', 'Download Client', '2005-02-03', NULL, 18150),
('BOBBYFUN', 'ACTNG', 'Bug fixing', '2024-02-14', NULL, 17990);
INSERT INTO works_on
VALUES (2, 'ADT4MFIA', 0.50),
(3, 'ROBOSPSE', 0.75),
(4, 'ROBOSPSE', 0.75),
(5, 'ROBOSPSE', 0.50),
(5, 'ADT4MFIA', 0.60),
(3, 'DNLDCLNT', 0.25);
SELECT employee_id
FROM works_on
WHERE 1.68 * assigned_time > 20;
while you mention that you're looking for person > 20hr per week, you don't give us information about what the "assigned_time" is in reference to. Is that a percentage of each day, week, month? With the values you have now, you can see that none of these individuals is close to a value of '20' if you just add your math to the select statement.
SELECT employee_id, (assigned_time * 1.68) as'worked'
FROM works_on
employee_id worked
2 0.8400
3 0.4200
3 1.2600
4 1.2600
5 1.0080
5 0.8400
I imagine you're looking for the sum of same employee id's working multiple times, which would be more along the lines of
SELECT employee_id, sum(assigned_time * 1.68) as'worked'
FROM works_on
group by employee_id
employee_id worked
2 0.8400
3 1.6800
4 1.2600
5 1.8480

ORDER by multiple queries in MySql

I am trying to build a chat list page where latest sent/received contact is shown at the top from one table. For this, I have a table smshistory where i store sent/received sms with numbers where one is company phone and other is client phone number
CREATE TABLE IF NOT EXISTS `smshistory` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fromnumber` varchar(20) NOT NULL,
`tonumber` varchar(20) NOT NULL,
`sms` varchar(20) NOT NULL,
`added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
--
-- Dumping data for table `smshistory`
--
INSERT INTO `smshistory` (`id`, `fromnumber`, `tonumber`, `sms`, `added`) VALUES
(1, 'companynum', 'client1num', 'Hello', '2021-07-16 12:28:23'),
(2, 'companynum', 'client2num', 'Hello', '2021-07-16 12:28:23'),
(3, 'companynum', 'client3num', 'Hello', '2021-07-16 12:28:23'),
(4, 'client1num', 'companynum', 'Hi', '2021-07-16 12:28:23'),
(5, 'companynum', 'client1num', 'Hello', '2021-07-16 12:28:23'),
(6, 'client1num', 'companynum', 'Hi', '2021-07-16 12:28:23'),
(7, 'client2num', 'companynum', 'Hi', '2021-07-16 12:28:23'),
(8, 'companynum', 'client2num', 'Hello', '2021-07-16 12:28:23'),
(9, 'client3num', 'companynum', 'Hi', '2021-07-16 12:28:23');
As first message will always be from company number, so I am showing DISTINCT list with:
SELECT DISTINCT (`tonumber`) FROM `smshistory` WHERE `fromnumber` = $companynum
Which gives me list like:
client1num
client2num
client3num
Requirement:
What I require is to show the DISTINCT with order of added DESC column in a way that if a client's number is in fromnumber or tonumber, it should show at top. So, according to my table, results should be:
client3num
client2num
client1num
Fiddle is at http://sqlfiddle.com/#!9/4256d1d/1
Any idea on how to achieve that?
In answer to your question you can use the following query:
SELECT distinct client_num from (
SELECT CASE WHEN fromnumber = 'companynum' THEN tonumber
ELSE fromnumber END client_num
FROM smshistory ORDER BY id DESC ) as a
For each of the rows that contain $companynum either in fromnumber or in tonumber you must extract the client's number with a CASE expression and use GROUP BY to remove duplicates.
Finally, sort the results by the max value of added:
SELECT CASE WHEN fromnumber = $companynum THEN tonumber ELSE fromnumber END client_num
FROM smshistory
WHERE $companynum IN (fromnumber, tonumber)
GROUP BY client_num
ORDER BY MAX(added) DESC
In answer to the question 'How do I sort a de-normalised collection of strings (that a follow a very constrained pattern) according to the numerals contained within those strings?', here's one method...
DROP TABLE IF EXISTS x;
CREATE TABLE x
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,bad_string VARCHAR(20) NOT NULL
);
INSERT INTO x VALUES (11,'client2num'),(17,'client3num'),(21,'client1num');
SELECT REPLACE(REPLACE(bad_string,'client',''),'num','') p FROM x;
+---+
| p |
+---+
| 2 |
| 3 |
| 1 |
+---+
...which can be rewritten as follows:
SELECT * FROM x ORDER BY REPLACE(REPLACE(bad_string,'client',''),'num','') DESC;
+----+------------+
| id | bad_string |
+----+------------+
| 17 | client3num |
| 11 | client2num |
| 21 | client1num |
+----+------------+

SQL search multitable and contains

Suppose that i have the following data:
CREATE TABLE IF NOT EXISTS `Class` (
`id` INT NOT NULL AUTO_INCREMENT,
`class name` CHAR(55) NOT NULL,
PRIMARY KEY (`id`)
);
REPLACE INTO `Class` (`id`,`class name`) VALUES
(1,'Mammalia'),(2, 'Amphibia'),(3, 'Aves'),(4, 'Reptile');
CREATE TABLE IF NOT EXISTS `Animals` (
`id` INT NOT NULL AUTO_INCREMENT,
`class_id` INT DEFAULT NULL,
`name` CHAR(55) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`class_id`) REFERENCES `Class`(`id`)
);
INSERT INTO `Animals` (`class_id`,`name`) VALUES
(1,'Przewalskis horse'),
(1,'Bos taurus'),
(1,'Sus scrofa'),
(1,'Panthera leo'),
(1,'Felis catus'),
(1,'Canis lupus'),
(1,'Pan troglodytes'),
(1,'Nasalis larvatus'),
(1,'Ailuropoda melanoleuca'),
(1,'Elephas maximus sumatranus'),
(1,'Panthera pardus orientalis'),
(2, 'Bufu Bufu'),
(2, 'Notophthalmus viridescens'),
(2, 'Dermophis mexicanus'),
(3, 'Campephilus melanoleucos melanoleucos'),
(3, 'Tyto alba'),
(3, 'Cathartes aura'),
(3, 'Serinus canaria'),
(3, 'Amazona aestiva'),
(3, 'Amazona Oratrix'),
(3, 'Anodorhynchus hyacinthinus'),
(3, 'Ara ararauna'),
(3, 'Ara chloropterus'),
(3, 'Strigops habroptilus'),
(4, 'Ameivula venetacaudus'),
(4, 'Chelonia mydas'),
(4, 'Caretta caretta');
I want to do a query with only one input html field that return the following results:
if i type: "Amphibia" returns:
-------------------
| Class | Animals |
-------------------
| Amphibia | Bufu Bufu |
| Amphibia | Notophthalmus viridescens |
| Amphibia | Dermophis mexicanus |
----------------------------------
if i type: "Amphibia Amazona" returns:
-------------------
| Class | Animals |
-------------------
| Amphibia | Bufu Bufu |
| Amphibia | Notophthalmus viridescens |
| Amphibia | Dermophis mexicanus |
| Aves | Amazona aestiva |
| Aves | Amazona Oratrix |
--------------------------
if i type: "caudu" returns
-------------------
| Class | Animals |
-------------------
| Reptile | Ameivula venetacaudus |
--------------------------
Sample codes in Fiddle are welcome!
Thankful right now
Considering that input_var is the variable that you will receive from your HTML, you can do the following query:
SELECT `class name`, name
FROM Animals
LEFT JOIN Class
ON (Class.id= Animals.class_id)
WHERE `class name` like concat('%',SUBSTRING_INDEX(SUBSTRING_INDEX(input_var, ' ', 1), ' ', -1),'%')
OR name like concat('%',SUBSTRING_INDEX(SUBSTRING_INDEX(input_var, ' ', 1), ' ', -1),'%')
OR `class name` like concat('%',SUBSTRING_INDEX(SUBSTRING_INDEX(input_var, ' ', 2), ' ', -1),'%')
OR name like concat('%',SUBSTRING_INDEX(SUBSTRING_INDEX(input_var, ' ', 2), ' ', -1),'%')
;
I am also considering that you can receive an input with the inversed animal/class order, like "Amazona Amphibia" instead of "Amphibia Amazona".
If may also consider if case sensitive is important to you. If not, you may use upper in both your input string and the table column.
Keep this as your base.
select id, `class name`, `name`
from animals
left join class
on (class.id= animals.class_id)
And for searching
select `class name`, `name` from
(select ...)
where concat("%", "$search1", "%") like `class name` or
concat("%", "$search2", "%") like `name`;
To split the first word (mysql)
substring_index(substring_index(var, ' ', 1), ' ', -1)
and for second word (mysql)
substring_index(substring_index(var, ' ', 2), ' ', -1)
In PHP you can use substr function.

Calculate difference timestamp based on status (on/off)

Im trying to calculate the totale (daily) uptime and the total downtime in MySQL. I got the follow database:
CREATE TABLE `IOT_DATA` (
`DATA_ID` int(11) NOT NULL,
`DATA_BRON` text NOT NULL,
`DATA_SOORT` text NOT NULL,
`DATA_WAARDE` int(11) NOT NULL,
`DATA_TIJD` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `IOT_DATA` (`DATA_ID`, `DATA_BRON`, `DATA_SOORT`, `DATA_WAARDE`, `DATA_TIJD`) VALUES
(931, '77', 'temperature', 29, '2019-06-11 14:05:00'),
(932, '77', 'humidity', 32, '2019-06-11 14:05:00'),
(933, '77', 'temperature', 30, '2019-06-11 14:10:01'),
(934, '77', 'humidity', 32, '2019-06-11 14:10:01'),
(935, '77', 'temperature', 30, '2019-06-11 14:15:00'),
(936, '77', 'humidity', 31, '2019-06-11 14:15:00'),
(963, '77', 'status', 0, '2019-06-11 14:17:19'),
(978, '77', 'status', 1, '2019-06-11 14:18:40'),
(982, '77', 'temperature', 29, '2019-06-11 14:20:00'),
(983, '77', 'humidity', 32, '2019-06-11 14:20:00'),
(993, '77', 'status', 0, '2019-06-11 14:22:40'),
(1008, '77', 'status', 1, '2019-06-11 14:23:21'),
(1019, '77', 'status', 0, '2019-06-11 14:24:50'),
(1044, '77', 'temperature', 30, '2019-06-11 14:25:00'),
(1045, '77', 'humidity', 32, '2019-06-11 14:25:00'),
ALTER TABLE `IOT_DATA`
ADD PRIMARY KEY (`DATA_ID`);
ALTER TABLE `IOT_DATA`
MODIFY `DATA_ID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1070;
It saves all sensor data from a machine. The 'DATA_SOORT' "status" indicates if the machine is started (1) or stopped (0).
I do not have a clue where to begin, I searched for comparable topics and got this:
select
date,
DATA_ID,
sum(datediff(minute, starttime, endtime )) downtime
from (
select
cast(t1.DATA_TIJD as date) as date,
t1.serviceid,
min(t1.DATA_TIJD) starttime,
ca.DATA_TIJD endtime
from IOT_DATA t1
cross apply (
select top 1 DATA_TIJD
from IOT_DATA
where serviceid = t1.serviceid
and DATA_TIJD > t1.DATA_TIJD
and DATA_SOORT = "status"
and DATA_WAARDE in (1)
order by DATA_TIJD
) ca
where t1.DATA_ID in (0)
group by cast(t1.DATA_TIJD as date), t1.DATA_ID, ca.DATA_TIJD
) a
group by date, DATA_ID;
It's not working, and honestly have no idea what it is. It gives the follow error:
- Incorrect parameter count in the call to native function 'datediff'
So i searched for that error and got something like it only can handle two expressions? But im not even sure if this is the correct way.
The expected result should be like
+------------+--------+----------+------------+--------------+
| Date | Source | TotaleUp | TotaleDown | PercentageUp |
+------------+--------+----------+------------+--------------+
| 11-06-2019 | 77 | 04:30:20 | 3:30:40 | 56% |
| 12-06-2019 | 77 | 06:00:00 | 1:00:00 | 85% |
+------------+--------+----------+------------+--------------+
The source = DATA_BRON
PercentageUP = Totaleup/(Totaleup+TotaleDown)*100
It is my first topic, if anything is missing please let me know

mysql select with time range and condition

error_tbl success_tbl
id | creationTime id|creationTime
1 2014-09-23 10:03:40 1212 2014-09-23 10:02:40
1213 2014-09-23 10:03:40
1214 2014-09-23 10:10:40
After run of query I want to have this:
result table
creationTime | fail_ids | succ_ids
2014-09-23 10:03:40, 1, NULL
2014-09-23 10:02:40, NULL, 1212
2014-09-23 10:03:40, NULL, 1213
I want to select all data (success and fail) within time range of EACH AND EVERY 5 minutes only if any record exists in error_tbl within that period.
(SELECT
creationTime,
id as fail_ids,null as succ_ids
FROM error_tbl a
UNION
SELECT
creationTime,
null as fail_ids ,id as succ_ids
FROM success_tbl b
) s
Given the simplified schema with minor changes to make it look clean.
CREATE TABLE error(
`error_id` INT,
`creation_time` DATETIME
);
INSERT INTO error(`error_id`, `creation_time`) VALUES
(1, '2014-09-23 10:03:40'),
(2, '2014-09-23 10:04:05'),
(3, '2014-09-24 10:08:04');
CREATE TABLE success(
`success_id` INT,
`creation_time` DATETIME
);
INSERT INTO success(`success_id`, `creation_time`) VALUES
(1212, '2014-09-23 10:02:40'),
(1213, '2014-09-23 10:03:40'),
(1214, '2014-09-23 10:10:40');
This should work, though I don't expect it to perform well on big datasets.
SELECT CONCAT(
DATE_FORMAT(creation_time, '%Y-%m-%d %k:'),
LPAD(FLOOR(MINUTE(creation_time) / 5) * 5, 2, 0),
':00'
) `creation_time`,
GROUP_CONCAT(error_id) `error_ids`,
GROUP_CONCAT(success_id) `success_ids`
FROM (
(
SELECT NULL success_id, error_id, creation_time
FROM error
)
UNION
(
SELECT success_id, NULL error_id, creation_time
FROM success
)
) un
GROUP BY FLOOR(UNIX_TIMESTAMP(creation_time) / (5 * 60))
HAVING error_ids IS NOT NULL
SQL Fiddle snippet.