MySQL select from view with user variables - Unexpected result - mysql

I have 2 tables and a view. In product_oper I have some products that I receive (when id_dest is 1) and that I sell (when id_src is 1). The table product_doc contains the date when the operation took place.
CREATE TABLE product_doc (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
doc_date date NOT NULL,
doc_no char(16) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
INSERT INTO product_doc (id,doc_date,doc_no) VALUES
(1,'2009-10-07','1'),
(2,'2009-10-14','2'),
(3,'2009-10-28','4'),
(4,'2009-10-21','3');
CREATE TABLE product_oper (
id bigint(12) unsigned NOT NULL AUTO_INCREMENT,
id_document bigint(20) unsigned NOT NULL,
prod_id bigint(12) unsigned NOT NULL DEFAULT '0',
prod_quant decimal(16,4) NOT NULL DEFAULT '1.0000',
prod_value decimal(18,2) NOT NULL DEFAULT '0.00',
id_dest bigint(20) unsigned NOT NULL,
id_src bigint(20) unsigned NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
INSERT INTO product_oper (id,id_document,prod_id,prod_quant,prod_value,id_dest,id_src)
VALUES
(10,1,1,'2.0000', '5.00',1,0),
(11,3,1,'0.5000', '1.20',0,1),
(12,1,2,'3.0000','26.14',1,0),
(13,2,2,'0.5000','10.20',0,1),
(14,3,2,'0.3000', '2.60',0,1),
(15,4,2,'1.0000', '0.40',1,0);
In the view I want to see all the operations and the dates.
CREATE VIEW product_oper_view AS
SELECT product_oper.*, product_doc.doc_date AS doc_date, product_doc.doc_no AS doc_no
FROM product_oper JOIN product_doc ON product_oper.id_document = product_doc.id
WHERE 1;
Now I want to see the operations of a single product, and the amount and value at a specific date.
SET #amount=0.000, #balance=0.00;
SELECT product_oper_view.*,
IF(id_dest<>0, prod_quant, NULL) AS q_in,
IF(id_dest<>0, prod_value, NULL) AS v_in,
IF(id_src<>0, prod_quant, NULL) AS q_out,
IF(id_src<>0, prod_value, NULL) AS v_out,
#amount:=#amount + IF(id_dest<>0, 1, -1)*prod_quant AS q_amount,
#balance:=#balance + IF(id_dest<>0, 1, -1)*prod_value AS v_balance
FROM product_oper_view
WHERE prod_id=2 AND (id_dest=1 OR id_src=1)
ORDER BY doc_date;
The result I get is strange:
id, id_ prod_ prod_ id_ id_ doc_date, q_in, v_in, q_ v_
doc, quant,value,dest,src, q_out, v_out, amount, balance
12, 1, 3.0000, 26.14, 1, 0, '2009-10-07', 3.0000, 26.14, NULL , NULL, 3.000, 26.14
13, 2, 0.5000, 10.20, 0, 1, '2009-10-14', NULL , NULL, 0.5000, 10.20, 2.500, 15.94
15, 4, 1.0000, 0.40, 1, 0, '2009-10-21', 1.0000, 0.40, NULL , NULL, 3.200, 13.74
14, 3, 0.3000, 2.60, 0, 1, '2009-10-28', NULL , NULL, 0.3000, 2.60, 2.200, 13.34
The amount starts from zero,
at row 1: +3 => 3 (ok)
at row 2: -0.5 => 2.5 (ok)
at row 3: +1 => 3.2 (???)
at row 4: -0.3 => 2.2 (???)
It seems that MySQL doesn't take the order of rows specified in the ORDER BY clause when executing the statement, and it looks after the id: See that document with id 4 is before document with id 3 ('2009-10-21' < '2009-10-28')
Am I doing something wrong, or is it a bug of MySQL?

If I'm not totally wrong the ORDER-operation is one of the last things done when preparing the result set. Therefore your calculations are done before ordering the results. The correct way to circumvent this problem should be to use a subselect:
SET #amount=0.000, #balance=0.00;
SELECT p.*,
#amount:=#amount + IF(p.id_dest <> 0, 1, -1) * p.prod_quant AS q_amount,
#balance:=#balance + IF(p.id_dest <> 0, 1, -1) * p.prod_value AS v_balance
FROM (
SELECT product_oper_view.*,
IF(product_oper_view.id_dest <> 0, product_oper_view.prod_quant, NULL) AS q_in,
IF(product_oper_view.id_dest <> 0, product_oper_view.prod_value, NULL) AS v_in,
IF(product_oper_view.id_src <> 0, product_oper_view.prod_quant, NULL) AS q_out,
IF(product_oper_view.id_src <> 0, product_oper_view.prod_value, NULL) AS v_out
FROM product_oper_view
WHERE product_oper_view.prod_id = 2
AND (product_oper_view.id_dest = 1 OR product_oper_view.id_src = 1)
ORDER BY product_oper_view.doc_date
) AS p

Related

How to build a mysql query that checks date and time being between two values?

I want to build a specific calendar feature.
As a basement I have the following MySQL table:
CREATE TABLE `presence` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`eid` int(10) unsigned DEFAULT NULL,
`start` timestamp NULL DEFAULT NULL,
`end` timestamp NULL DEFAULT NULL,
`frequence` int(11) DEFAULT NULL,
`fulltime` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
with the following example data:
INSERT INTO `presence` (`id`, `eid`, `start`, `end`, `frequence`, `fulltime`) VALUES (1, 14, "2021-11-29 10:00:00", "2021-12-13 18:00:00", NULL, NULL);
INSERT INTO `presence` (`id`, `eid`, `start`, `end`, `frequence`, `fulltime`) VALUES (2, 14, "2021-11-29 10:00:00", "2021-12-13 18:00:00", 1, NULL);
INSERT INTO `presence` (`id`, `eid`, `start`, `end`, `frequence`, `fulltime`) VALUES (3, 14, "2021-11-29 10:00:00", "2021-12-13 18:00:00", 1, 1);
INSERT INTO `presence` (`id`, `eid`, `start`, `end`, `frequence`, `fulltime`) VALUES (4, 14, "2021-11-29 10:00:00", "2021-12-13 18:00:00", NULL, 1);
INSERT INTO `presence` (`id`, `eid`, `start`, `end`, `frequence`, `fulltime`) VALUES (5, 14, "2021-11-29 10:00:00", NULL, 1, 1);
In my app environment I have the following examples:
row id#1 means, that an employee (eid: 14) is present every day from 2021-11-29 to 2021-12-13. Each day from 10 am to 6 pm.
row id#2 means, that an employee (eid: 14) is present every monday from 2021-11-29 to 2021-12-13. Each Monday from 10 am to 6 pm. Monday because start is a Monday and because frequence = 1.
row id#3 means, that an employee (eid: 14) is present every monday from 2021-11-29 to 2021-12-13. Each Monday the entire day, because fullday = 1. Thus, ignoring the time.
row id#4 means, that an employee (eid: 14) is present every day from 2021-11-29 to 2021-12-13. Each day the entire day, because fullday = 1 tells again to ignore the time.
row id#5 finally has an open end for the presence. It means the employee is present every Monday from 2021-11-29 until infinity.
I am working on the following query the entire day and I'm at stage where I can't tell what I expect anymore...
SET #date = "2021-12-13 19:00:00";
SET #fullday = 0;
SELECT start, end, frequence, fulltime
FROM `presence`
WHERE (
IF (#fullday = 1,
DATE(#date) >= DATE(`start`)
,
DATE(#date) >= DATE(`start`) AND
TIME(#date) >= TIME(`start`)
)
)
AND (
IF (#fullday = 1,
DATE(#date) >= DATE(`end`)
,
DATE(#date) >= DATE(`end`) AND
TIME(#date) >= TIME(`end`)
)
)
AND (
`frequence` IS NULL OR
`frequence` = 1 AND WEEKDAY(#date) = WEEKDAY(`start`)
)
In my app I will have a php function employee_is_present(int $eid, string $date): bool where $date can be either Y-m-d H:i:s or just Y-m-d, to check if an employee is generally available on a day (not having a time) or at a Very specific time. This is what I try to mimic using SET #fullday = 0|1;
According to the input, date could be either with or without time, it is easier to separate these two situations.
We can do that since we are using function call to query
Checking date only could use this query
SET #eid =14;
SET #date = DATE("2021-12-13");
SELECT *
FROM presence
WHERE #date >= DATE(`start`) AND ( `end` IS NULL OR #date <= DATE(`end`))
AND (`frequence` IS NULL OR (`frequence` = 1 AND WEEKDAY(`start`) = WEEKDAY(#date)))
AND eid = #eid
Checking with time, we use the above query result and filter the time
SET #eid =14;
SET #date = DATE("2021-12-13 19:00:00");
SET #time = TIME(#date);
SELECT *
FROM (
SELECT *
FROM presence
WHERE #date >= DATE(`start`) AND ( `end` IS NULL OR #date <= DATE(`end`))
AND (`frequence` IS NULL OR (`frequence` = 1 AND WEEKDAY(`start`) = WEEKDAY(#date)))
AND eid = #eid
) f
WHERE `fulltime` = 1 OR (TIME(`start`) <= #time AND #time <= TIME(`end`))
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=3e207fed77e34a6b458e79764e382156

Find closest subdistrict to point on map

I have table metro_station:
CREATE TABLE metro_station (
metro_station_id int(11) NOT NULL DEFAULT '0',
metro_station_name varchar(25) NOT NULL DEFAULT '',
lng double(12,8) NOT NULL DEFAULT '0.00000000',
lat double(12,8) NOT NULL DEFAULT '0.00000000',
PRIMARY KEY (metro_station_id),
KEY metro_line_id (metro_line_id)
)
INSERT INTO metro_station (metro_station_id, metro_station_name, lng, lat) VALUES
(101, 'Name1', 27.53907013, 53.88590000),
(102, 'Name2', 27.54864857, 53.89176877);
AND Table Subdistricts:
CREATE TABLE town_subdistrict (
town_subdistrict_id int(11) NOT NULL DEFAULT '0',
town_id int(11) NOT NULL DEFAULT '0',
gis polygon DEFAULT NULL,
PRIMARY KEY (town_subdistrict_id,town_id),
)
It's example for polygon:
GeomFromText('POLYGON((27.68961910000009 53.85629640000011,27.6895764 53.85631860000004,27.68929950000009 53.85646880000002,27.68877430000001 53.8567605000001,27.68861040000004 53.8568457,27.68544530000002 53.85830420000002,27.68500580000005 53.85851960000003,27.68492000000004 53.8585892,27.68481260000006 53.85874140000007,27.68458750000006 53.85946250000008,27.68430890000006 53.86093070000005,27.68414780000008 53.86159519999996,27.6842887000001 53.86172090000009,27.68448770352082 53.86193099132333,27.68225238075777 53.85976079891942,27.67841497878083 53.85585953344282,27.67379448487603 53.85128354900957,27.6686035596248 53.84600035655184,27.66775455453183 53.84496766955237,27.66753322480145 53.84469845019371,27.66679610000001 53.84380180000007,27.66783560000009 53.84443690000007,27.66944540000004 53.84336060000003,27.67098580000009 53.84200109999997,27.67248260000003 53.84017040000015,27.67406100000008 53.83824710000003,27.6763772000001 53.83757050000008,27.67734710000004 53.83615390000004,27.67862339999999 53.83496679999995,27.67909450000011 53.83404369999998,27.6789687000001 53.83313210000006,27.67847662680461 53.83272019097144,27.69263016829434 53.83201716330638,27.69400539037001 53.82958231146308,27.69385479649461 53.82727139785728,27.6926500454914 53.82513813368487,27.68828282310418 53.82282697491387,27.68331322521536 53.82078238203517,27.68032580000007 53.81892670000002,27.67986200000003 53.81858860000009,27.67910950000001 53.81792580000012,27.67828620000011 53.81719320000005,27.67753390000013 53.81651950000003,27.67722040000001 53.81625219999998,27.67655940000009 53.81561589999992,27.67580240000011 53.81495020000008,27.6750387000001 53.81430539999997,27.67418070000007 53.81353240000009,27.67399120000006 53.81340409999999,27.67213690000017 53.81160090000013,27.67161260000005 53.81100900000003,27.67142320000011 53.8107294,27.67024680000015 53.8088484,27.67135070205039 53.80848121192794,27.67488737639733 53.80777385908746,27.68201776822574 53.80582015590466,27.68532627003423 53.80531487302213,27.68812138363106 53.80487695626488,27.68971859140055 53.80494432837148,27.69057423842 53.80514644404158,27.69365456768981 53.80592121175043,27.69519473232477 53.80666228050963,27.69901662234492 53.80925591801111,27.70494910834612 53.81346602682155,27.70837169642382 53.81552040639381,27.71316331973261 53.81777674004643,27.71264993152094 53.81821452205632,27.70695040000009 53.82211469999999,27.70701040000001 53.82255270000004,27.70702220000012 53.82268390000014,27.70701880000003 53.82323590000005,27.70689450000014 53.82376860000005,27.70667430000002 53.82419530000004,27.70614210000008 53.82510090000006,27.7019328 53.82809180000003,27.70013020000012 53.82938730000003,27.69772210000011 53.83105480000007,27.69648220327563 53.83182582490014,27.69612568368424 53.83245702810299,27.69719524245847 53.83334069660663,27.70054147070045 53.83540405953607,27.70430138456548 53.83787092626668,27.7054823282021 53.84078545245671,27.70753006210112 53.84583818257505,27.70801748194182 53.84704069019841,27.70821247348324 53.8473004850166,27.70828194328709 53.84739304147723,27.69748263140682 53.85265565096212,27.6974264419219 53.85258549112009,27.6972124000001 53.85268900000007,27.69685850000002 53.8528578,27.69519809999999 53.85363349999992,27.69528360000001 53.85369800000001,27.69511160000011 53.85378049999996,27.69502910000013 53.85372320000008,27.68961910000009 53.85629640000011))',0)
This data is is used for map display. I need find closest subdistrict to metro station.
SELECT
metro_station_id, metro_station_name,
(SELECT town_subdistrict_id
FROM town_subdistrict
WHERE ST_DISTANCE(gis, CONCAT('Point(', lng, ' ', lat, ')')) < 1
LIMIT 1) as subdistrict_id
FROM metro_station
WHERE 1
This query always returns null. How can I fix it?
You need convert your POINT to geom with ST_GeomFromText:
SQL DEMO
SELECT
metro_station_id, metro_station_name,
(SELECT town_subdistrict_id
FROM town_subdistrict
WHERE ST_DISTANCE(gis, ST_GeomFromText(CONCAT('Point(', lng, ' ', lat, ')'))) < 1
LIMIT 1) as subdistrict_id
FROM metro_station

MySQL query to select from different tables and group data [duplicate]

This question already has answers here:
How do I generate nested json objects using mysql native json functions?
(4 answers)
Closed 6 years ago.
I have some tables about tv shows. Also I have one of the pages where I show where user can see some seasons and episodes.
CREATE TABLE `tv` (
`tv_id` int(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`title` VARCHAR(30),
`rating` float(2,1) DEFAULT NULL,
`total_seasons` tinyint UNSIGNED,
)
CREATE TABLE `tv_player_episode_mapping` (
`tv_id` int(11) UNSIGNED,
`season` tinyint(11) UNSIGNED,
`episode` tinyint(11) UNSIGNED,
`player_id` tinyint(11) UNSIGNED,
`file_name` TEXT
);
CREATE TABLE `player` (
`player_id` int(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`hostname` VARCHAR(30),
UNIQUE KEY `name` (`hostname`)
);
To select a data from a table I use this query:
SELECT tpe.season, tpe.episode, p.hostname, tpe.file_name, t.rating, t.total_seasons
FROM tv_player_episode_mapping tpe
INNER JOIN player p ON p.player_id = tpe.player_id
INNER JOIN tv t ON t.tv_id = tpe.tv_id
WHERE tpe.tv_id = 1;
Below is the result of query and here the problem - some data like total_seasons and ratins repeated:
[
{
file_name:"19891-molodoy-papa.html",
hostname:"kinoclub.cc",
season: 1,
episode: 1,
total_seasons: 1,
rating: 8.5,
},
{
file_name:"19891-molodoy-papa.html",
hostname:"kinoclub.cc",
season: 1,
episode: 2,
total_seasons: 1,
rating: 8.5,
}
]
I try to change my query to get somethink like this (I am novice in databases and I think this is good structure for response from the server):
{
total_seasons: 1,
rating: 8.5,
players: [
[ // Here first element of array `playes` is array for season 1
[ // Episode 1
{
file_name:"1-1-the-young-pope.html",
hostname:"my-player.io",
},
{
file_name:"1-1-the-young-pope.html",
hostname:"another-one-player.io",
}
],
[ // Episode 2
{
file_name:"1-2-the-young-pope.html",
hostname:"my-player.io",
},
{
file_name:"1-2-the-young-pope.html",
hostname:"another-one-player.io",
}
],
],
[] // Season 2
]
};
But because I am little experienced in databases I stop trying with this query and can't find the way:
SELECT tpe.season, tpe.episode,
JSON_OBJECT(
'total_seasons', t.total_seasons,
'players', JSON_OBJECT('hostname', p.name, 'file_name', tpe.file_name))
FROM tv_player_episode_mapping tpe
INNER JOIN player p ON p.player_id = tpe.player_id
INNER JOIN tv t ON t.tv_id = tpe.tv_id
WHERE tpe.tv_id = 1;
How can I change this query to solve my problem?
UPD:
I wrote some query using example and I am good with it, but now result of my query looks strange.
New query:
select json_object(
'rating', t.rating,
'total_seasons', t.total_seasons,
'players', json_array(
(select GROUP_CONCAT(
json_object(
'hostname', p.hostname,
'file_name', tpe.file_name,
'season', tpe.season,
'episode', tpe.episode
)
)
FROM tv_player_episode_mapping tpe
INNER JOIN player p ON p.player_id = tpe.player_id
INNER JOIN tv t ON t.tv_id = tpe.tv_id
where tpe.tv_id = 52)
)
)
from tv t WHERE tv_id=52;
Result of query:
{
"json_object( 'rating', t.rating, 'total_seasons', t.total_seasons, 'players', json_array( (select GROUP_CONCAT( json_object( 'hostname', p.hostname, 'file_name', tpe.file_name, 'season', tpe.season, 'episod":
"{\"players\": [\"{\\\"season\\\": 1, \\\"episode\\\": 1, \\\"hostname\\\": \\\"kinoclub.cc\\\", \\\"file_name\\\": \\\"19891-molodoy-papa.html\\\"},{\\\"season\\\": 1, \\\"episode\\\": 2, \\\"hostname\\\": \\\"kinoclub.cc\\\", \\\"file_name\\\": \\\"19891-molodoy-papa.html\\\"},{\\\"season\\\": 1, \\\"episode\\\": 1, \\\"hostname\\\": \\\"kinoclub.cc\\\", \\\"file_name\\\": \\\"123.html\\\"}\"], \"rating\": 8.5, \"total_seasons\": 1, \"rating\": 8.300000190734863}"
}
What can I do to format result like this?
{
total_seasons: 1,
rating: 8.5,
players: []
};
The short answer is no, but you still can partially implement JSON like structure by using group_concat function.
Example:
SELECT x, y, concat('[', group_concat(z), ']') as z FROM tbl group by x, y;
Will give you answer like:
x y z
1 2 [3,4,5]
6 7 [8,9,10]

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.

SQL Server Tree Query

I need some help is MS SQL Server Query. I’m not much of a DBA. I have an application with an Organization Table which is made up of a parent-child relationship:
CREATE TABLE [dbo].[Organizations](
[OrgPK] [int] IDENTITY(1,1) NOT NULL,
[OrgParentFK] [int] NULL,
[OrgName] [varchar](200) NOT NULL,
CONSTRAINT [PK__Organizations] PRIMARY KEY CLUSTERED
Sample data looks like this:
OrgPK, OrgParentFK, OrgName
1, 0, Corporate
2, 1, Department A
3, 1, Department B
4, 2, Division 1
5, 2, Division 2
6, 3, Division 1
7, 6, Section 1
8, 6, Section 2
I'm trying to generate a query that returns an org path based on a given OrgPK. Example if given OrgPK = 7 the query would return 'Corporation/Department B/Division 1/Section 1'
If give OrgPk = 5 the return string would be 'Corporation/Department A/Division 2'
Thank you for your assistance.
WITH OrganizationsH (OrgParentFK, OrgPK, OrgName, level, Label) AS
(
SELECT OrgParentFK, OrgPK, OrgName, 0, CAST(OrgName AS VARCHAR(MAX)) As Label
FROM Organizations
WHERE OrgParentFK IS NULL
UNION ALL
SELECT o.OrgParentFK, o.OrgPK, o.OrgName, level + 1, CAST(h.Label + '/' + o.OrgName VARCHAR(MAX)) As Label
FROM Organizations o JOIN OrganizationsH h ON o.OrgParentFK = h.OrgPK
)
SELECT OrgParentFK, OrgPK, OrgName, level, Label
FROM OrganizationsH
WHERE OrgPK = 5
h/t to marc_s
It can also be solved by creating a scalar valued function:
-- SELECT [dbo].[ListTree](5)
CREATE FUNCTION [dbo].[ListTree](#OrgPK int)
RETURNS varchar(max)
AS
BEGIN
declare #Tree varchar(MAX)
set #Tree = ''
while(exists(select * from dbo.Organizations where OrgPK=#OrgPK))
begin
select #Tree=OrgName+'/'+#Tree,
#OrgPK=OrgParentFK
from dbo.Organizations
where OrgPK=#OrgPK
end
return left(#Tree,len(#Tree)-1)
END