MySQL query comparing values to previous rows' values - mysql

I've been searching but have been unable to find a solution to this--I know it's do-able but I just don't have the ninja SQL skills I need (yet)....
I'm looking for a solution to this issue: I have a 2 tables related to stock market data. The first is a simple list of stock symbols with an ID and stock ticker symbol (ID,SYMBOL). The second table contains historical price data for each of the stocks. (ID, DATE, OPEN, HIGH, LOW, CLOSE, VOLUME).
I'm trying to figure out how to query for stocks that have the most recent CLOSE price that is greater than their CLOSE price 5 trading-days ago. I can't just do date math because the stocks don't trade every day (no trading on weekends & holidays, as well as some stocks may not trade on a normal trading day). Thus, I just need to compare the CLOSE price from most recent row and the 5th row proceeding it for each symbol.
I have sample tables and data here:
http://sqlfiddle.com/#!2/5fe76/2
CREATE TABLE `STOCKS` (
`ID` int,
`SYMBOL` varchar(10)
);
INSERT INTO `STOCKS` (`ID`,`SYMBOL`)
VALUES
(1, 'AA'),
(2, 'ADT'),
(3, 'AEO'),
(4, 'AFA');
CREATE TABLE `PRICES` (
`ID` int,
`DATE` date,
`OPEN` decimal(6,2),
`HIGH` decimal(6,2),
`LOW` decimal(6,2),
`CLOSE` decimal(6,2),
`VOLUME` bigint
);
INSERT INTO `PRICES` (`ID`,`DATE`,`OPEN`,`HIGH`,`LOW`,`CLOSE`,`VOLUME`) VALUES
(1, '2014-11-06', 16.37, 16.42, 16.15, 16.37, 14200400),
(1, '2014-11-05', 16.68, 16.69, 16.17, 16.26, 18198200),
(1, '2014-11-04', 16.85, 16.87, 16.43, 16.56, 13182800),
(1, '2014-11-03', 16.78, 17.03, 16.65, 16.93, 15938500),
(1, '2014-10-31', 16.43, 16.76, 16.24, 16.76, 18618300),
(1, '2014-10-30', 16.17, 16.36, 15.83, 16.22, 17854400),
(1, '2014-10-29', 16.58, 16.70, 16.05, 16.27, 31173000),
(1, '2014-10-28', 16.5, 16.65, 16.41, 16.60, 12305900),
(1, '2014-10-27', 16.56, 16.57, 16.31, 16.38, 15452900),
(1, '2014-10-24', 16.33, 16.57, 16.22, 16.55, 12840200),
(2, '2014-11-06', 35.9, 36.12, 35.75, 36.07, 1018100),
(2, '2014-11-05', 35.68, 35.99, 35.37, 35.96, 1101500),
(2, '2014-11-04', 35.13, 35.69, 35.02, 35.49, 819100),
(2, '2014-11-03', 35.81, 35.99, 35.27, 35.32, 1304500),
(2, '2014-10-31', 35.79, 35.86, 35.46, 35.84, 1319400),
(2, '2014-10-30', 34.7, 35.34, 34.66, 35.19, 1201800),
(2, '2014-10-29', 35.06, 35.56, 34.5, 34.92, 1359000),
(2, '2014-10-28', 34.32, 35.17, 34.15, 35.07, 1301800),
(2, '2014-10-27', 34.2, 34.2, 33.66, 34.1, 662600),
(2, '2014-10-24', 34.02, 34.54, 33.95, 34.5, 750600),
(3, '2014-11-06', 13.27, 13.92, 13.25, 13.82, 6518000),
(3, '2014-11-05', 12.95, 13.27, 12.74, 13.22, 8716700),
(3, '2014-11-04', 12.85, 12.94, 12.65, 12.89, 4541200),
(3, '2014-11-03', 12.91, 13.12, 12.73, 12.89, 4299100),
(3, '2014-10-31', 13.2, 13.23, 12.83, 12.87, 7274700),
(3, '2014-10-30', 12.83, 12.91, 12.68, 12.86, 4444300),
(3, '2014-10-29', 13.02, 13.20, 12.79, 12.91, 2974900),
(3, '2014-10-28', 12.87, 13.10, 12.52, 13.04, 7365600),
(3, '2014-10-27', 12.84, 13.00, 12.67, 12.92, 6647900),
(3, '2014-10-24', 13.26, 13.29, 12.60, 12.92, 12803300),
(4, '2014-11-06', 24.59, 24.59, 24.49, 24.55, 20400),
(4, '2014-11-05', 24.81, 24.9, 24.81, 24.88, 11800),
(4, '2014-11-04', 24.87, 24.88, 24.76, 24.88, 10600),
(4, '2014-11-03', 24.85, 24.88, 24.76, 24.81, 18100),
(4, '2014-10-31', 24.82, 24.85, 24.77, 24.78, 8100),
(4, '2014-10-30', 24.83, 24.87, 24.74, 24.79, 13900),
(4, '2014-10-29', 24.86, 24.86, 24.78, 24.81, 5500),
(4, '2014-10-28', 24.85, 24.85, 24.80, 24.84, 10600),
(4, '2014-10-27', 24.68, 24.85, 24.68, 24.85, 7700),
(4, '2014-10-24', 24.67, 24.82, 24.59, 24.82, 9300);
Pseudo code for the query would be something like this:
"Find symbols whos most recent closing prices is greater than the closing price 5 trading-days earlier"
The query I'd like to create should result in the following:
Date Symbol Close Close(-5)
2014-11-06 AA 16.37 16.22
2014-11-06 ADT 36.07 35.19
2014-11-06 AEO 13.82 12.86
(the symbol 'AFA' would not match as it's recent close is 24.55 and 5 rows prior it was 24.75)

You can get the price 5 days ago using a correlated subquery. In fact, you can get the most recent price the same way. So, this might be the right path:
select s.*,
(select p.close
from prices p
where p.id = s.id
order by date desc
limit 1
) as Close,
(select p.close
from prices p
where p.id = s.id and p.date <= date(now()) - interval 5 day
order by date desc
limit 1
) as Close_5
from stocks s
having Close > Close_5;

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

MYSQL sum() with exclusion of records

I need to calculat fin_sum for visit_numbers and industry_code, but if id in other table then i need to exclude records which have id in other table from calculation but, if id is not esixst in table themn it should be included.
I have table with next structure
create temporary table client_transactions_final
(
id int,
fin_amount decimal (6,2),
ind_code varchar(10),
visit_number int
);
insert into client_transactions_final values
(1, 100, 'Ind 1', 2),
(1, 300, 'Ind 2', 3),
(2, 300, 'Ind 3', 4),
(2, 100, 'Ind 1', 2),
(3, 300, 'Ind 2', 3),
(4, 300, 'Ind 3', 5),
(5, 100, 'Ind 1', 2),
(6, 300, 'Ind 2', 5),
(6, 300, 'Ind 3', 4)
create temporary table term_map
(
id int
);
insert into term_map values
(2),
(4);
from this table i am runing select which do sum groyp by visit_numbers and industry code
SELECT visit_number,
case when id in (SELECT id FROM term_map) then
--sum(fin_amount) do not include ids from SELECT id FROM term_map into
else
SUM(fin_amount) end revenue
FROM
client_transactions_final
GROUP BY visit_number , ind_code
Howewer i need to calculat fin_sum for visit_numbers and industry_code, but if id in other table then i need to exclude records which have id in other table from calculation but, if id is not esixst in table themn it should be included. I alreade try difrentaproaches on how to do it but non of it is working any ideas how to do it ?
Try this:-
SELECT visit_number,
SUM(fin_amount) revenue
FROM
client_transactions_final
where id not in (SELECT id FROM term_map)
GROUP BY visit_number , ind_code

Using mysql: column count doesn't match value count at row 1

I'm new to mysql, and can not figure out why this error keeps coming up. It's a simple table and I want id to be 1, 2, 3, 4 etc. alongside two other columns. Why does it keep reading, column count doesn't match value count at row 1?
CREATE DATABASE thedatabase;
USE thedatabase;
CREATE TABLE cars (
id INTEGER AUTO_INCREMENT,
model INTEGER NOT NULL,
mileage INTEGER NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO thedatabase.cars (
model,
mileage
) VALUES (
(45, 34598),
(22, 23847),
(10, 3847),
(487, 93229),
(237, 238975),
(23, 23987),
(34, 3498),
(57, 34984),
(56, 34983),
(20, 9845);
You have got an extra opening bracked in INSERT statement, after VALUES below should work fine:
INSERT INTO thedatabase.cars ( model,
mileage ) VALUES (45, 34598), (22, 23847), (10, 3847), (487, 93229), (237, 238975), (23, 23987), (34, 3498), (57, 34984), (56, 34983), (20, 9845);

Select/Query calculate time between timestamps without weekends

Problem presented is to calculate for each row returned the time ("ResponseTime") between 2 timestamps ("StartDateTime" and "EndDateTime") excluding the weekends. Does not take into consideration Work hours or Holidays.
Weekends in this case are defined as Saturday 00:00:00 to Sunday 23:59:59.
Had a tough time coming up with a solution for this question so thought I would share my final product. Found lots of solutions online but most either used a calendar table, which I couldn't use in this application, or had a logic I didn't understand. Solution shared below. Please feel free to offer your own solution based on the problem or to correct any errors you see in my code. Regards,
EDIT: as per comments provided by #JuanCarlosOropeza solution I presented is not optimal. Providing sample data for him to forward a different solution. If anyone has improvements as well feel free to participate.
CREATE TABLE SourceTable
(`id` int, `StartDateTime` datetime, `EndDateTime` datetime)
;
INSERT INTO SourceTable
(`id`, `StartDateTime`, `EndDateTime`)
VALUES
(1, '2016-09-20 12:52:00', '2016-09-23 13:15:00'),
(2, '2016-09-19 19:15:00', '2016-09-22 19:15:00'),
(3, '2016-09-01 10:35:00', '2016-09-06 13:15:00'),
(4, '2016-09-26 10:34:00', '2016-09-29 11:25:00'),
(5, '2016-09-01 13:01:00', '2016-09-06 14:55:00'),
(6, '2016-09-05 02:21:00', '2016-09-08 19:15:00'),
(7, '2016-09-27 14:14:00', '2016-10-01 19:15:00'),
(8, '2016-09-27 04:18:00', '2016-09-30 14:15:00'),
(9, '2016-09-01 14:50:00', '2016-09-06 17:25:00'),
(10, '2016-09-20 12:52:00', '2016-09-23 13:15:00'),
(11, '2016-09-26 02:14:00', '2016-09-29 10:15:00'),
(12, '2016-09-01 12:04:00', '2016-09-06 17:05:00'),
(13, '2016-09-20 15:30:00', '2016-09-23 15:15:00'),
(14, '2016-09-02 16:04:00', '2016-09-07 20:55:00'),
(15, '2016-09-23 10:41:00', '2016-09-28 13:05:00'),
(16, '2016-09-27 16:28:00', '2016-10-01 13:15:00'),
(17, '2016-09-27 15:33:00', '2016-10-01 22:45:00'),
(18, '2016-09-20 12:53:00', '2016-09-23 13:25:00'),
(19, '2016-09-19 13:49:00', '2016-09-22 13:05:00'),
(20, '2016-09-20 13:46:00', '2016-09-23 13:15:00'),
(21, '2016-09-01 16:32:00', '2016-09-06 18:05:00'),
(22, '2016-09-01 10:35:00', '2016-09-06 22:45:00'),
(23, '2016-09-26 12:40:00', '2016-09-29 12:35:00'),
(24, '2016-09-27 10:37:00', '2016-09-30 21:25:00'),
(25, '2016-09-27 09:41:00', '2016-09-30 15:15:00'),
(26, '2016-09-16 02:09:00', '2016-09-21 10:05:00'),
(27, '2016-09-20 15:13:00', '2016-09-23 15:15:00'),
(28, '2016-09-20 15:30:00', '2016-09-23 15:15:00'),
(29, '2016-09-27 09:55:00', '2016-09-30 13:25:00'),
(30, '2016-09-27 04:18:00', '2016-09-30 14:15:00')
;
I created this solution considering the following logic assumptions.
StartDateTime always occurs before EndDateTime (though had some that didn't and it calculated the time difference correctly)
Week StartDateTime occurred: WEEK(StartDateTime,1)
Week EndDateTime occurred: WEEK(EndDateTime,1)
Start of weekend of week StartDateTime: ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime))
Start of workweek after first weekend: ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),7-WEEKDAY(StartDateTime))
Full Query:
SELECT
id,
StartDateTime,
EndDateTime,
CASE
WHEN ( WEEK(EndDateTime,1) = WEEK(StartDateTime,1) )
THEN
CASE
WHEN ( StartDateTime >= ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)) )
THEN SEC_TO_TIME(0)
ELSE
CASE
WHEN ( EndDateTime >= ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)) )
THEN ( TIMEDIFF(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)),StartDateTime) )
ELSE ( TIMEDIFF(EndDateTime,StartDateTime) )
END
END
ELSE
CASE
WHEN ( StartDateTime >= ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)) )
THEN
CASE
WHEN ( EndDateTime >= ADDDATE(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)),(WEEK(EndDateTime,1) - WEEK(StartDateTime,1)) * 7) )
THEN ( SEC_TO_TIME(120*3600*(WEEK(EndDateTime,1) - WEEK(StartDateTime,1))) )
ELSE ( SEC_TO_TIME(120*3600*(WEEK(EndDateTime,1) - WEEK(StartDateTime,1) - 1) + TIME_TO_SEC(TIMEDIFF(EndDateTime, ADDDATE(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),7-WEEKDAY(StartDateTime)),7*(WEEK(EndDateTime,1) - WEEK(StartDateTime,1) - 1))))) )
END
ELSE
CASE
WHEN ( EndDateTime >= ADDDATE(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)),(WEEK(EndDateTime,1) - WEEK(StartDateTime,1)) * 7) )
THEN ( SEC_TO_TIME(120*(WEEK(EndDateTime,1) - WEEK(StartDateTime,1)) + TIME_TO_SEC(TIMEDIFF(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)),StartDateTime))) )
ELSE ( SEC_TO_TIME(TIME_TO_SEC(TIMEDIFF(EndDateTime, ADDDATE(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),7-WEEKDAY(StartDateTime)),7*(WEEK(EndDateTime,1) - WEEK(StartDateTime,1) - 1)))) + TIME_TO_SEC(TIMEDIFF(ADDDATE(TIMESTAMP(DATE(StartDateTime),'00:00:00'),5-WEEKDAY(StartDateTime)),StartDateTime))) )
END
END
END as ResponseTime
FROM
SourceTable;
First CASE checks if both timestamps happened on the same week. Second layer checks if StartDateTime happened during the first weekend. Third layer checks if EndDateTime happened during a weekend. Based on these considerations outputs the correct calculation.

single table reccuring relatives

Can't name it less confusing, sorry...
Imagine the DB table with 3 columns:
object_id - some entity,
relation_key - some property of the object,
bundle_id - we must generalize different objects with this id.
Table has unique key for [object_id, relation_key]:
single object can't have duplicated relation_key,
but different objects can have equal relation_key
Some oxygen understanding with the picture:
Plenty objects can have deep relations by relation_key, all this objects will be related with bundle_id
How can I update bundle_id column with correct values using just single query?
I can write procedure but this way is unsuitable for me.
I look for statement like:
"UPDATE example [join example ON ...] SET bundle_id = ... WHERE ..."
there is "before" schema for mysql:
CREATE TABLE `example` (
`bundle_id` INT(11) DEFAULT NULL,
`object_id` INT(11) NOT NULL,
`relation_key` INT(11) NOT NULL,
PRIMARY KEY (`object_id`,`relation_key`)
);
INSERT INTO `example`(`object_id`, `relation_key`)
VALUES (1, 4), (1, 5), (1, 6), (2, 6), (2, 7), (2, 8), (3, 4),
(3, 9), (3, 10), (4, 11), (4, 12), (4, 13), (5, 14), (5, 15), (5, 16), (6, 17), (6, 11), (6, 18);
Here is the example "before": fiddle example (sqlfiddle stuck for this moment)
And "after" will look like like if you do the queries :
UPDATE `example` SET `bundle_id` = 1 WHERE `object_id` IN (1, 2, 3);
UPDATE `example` SET `bundle_id` = 2 WHERE `object_id` IN (4, 6);
UPDATE `example` SET `bundle_id` = 3 WHERE `object_id` IN (5);
object1 related to object2 by key=6,
object3 related TO object1 by key=4,
so ... objs 1, 2, 3 are related together.
here must be first bundle_id=1.
there is no other keys linking another objects to 1, 2, 3
object_id=4 related to object_id=6 by key=11
so ... obj [4, 6] are related together.
here must be second bundle_id=2,
there is no other keys linking another objects to 4, 6
object_id=5 has no relations to other objects
all object's key belong to itself.
here must be second bundle_id=3,
there is no other keys linking another objects to 5