Same stored procedure, same tables, different DB gives different result - mysql

I have stored procedure which gives me expected result when I run it on MySQL 5. But when I tried to run same procedure on MariaDB 10.1.22, it gives me a different result.
Here is my stored procedure -
DELIMITER ;;
CREATE DEFINER=`mconnect_admin`#`%` PROCEDURE `TestCumulative`(IN
start_date TIMESTAMP,IN end_date TIMESTAMP,IN duration TEXT,IN
mno_id TEXT,IN profile_type TEXT,IN timezone TEXT)
BEGIN
SET #provisioned = 0;
SET #downloaded = 0;
SET #excludeProfileFilter = FALSE;
SET #hourlyReport = FALSE;
SET #monthlyReport = FALSE;
SET #sdate = start_date;
DROP TABLE IF EXISTS tempDates;
CREATE TEMPORARY TABLE IF NOT EXISTS tempDates(timeRange
VARCHAR(50));
DELETE FROM tempDates;
IF(profile_type = '')
THEN
SET profile_type = null;
SET #excludeProfileFilter = TRUE;
END IF;
IF (duration = 'lastDay')
THEN
SET #hourlyReport=true;
END IF;
IF (duration = 'lastYear')
THEN
SET #monthlyReport = TRUE;
END IF;
WHILE #sdate <= end_date DO
IF (#hourlyReport = TRUE)
THEN
INSERT INTO tempDates (timeRange) VALUES (HOUR(#sdate) + 1);
SET #sdate = date_add(#sdate, INTERVAL 1 HOUR);
ELSE IF(#monthlyReport = TRUE)
THEN
INSERT INTO tempDates (timeRange) VALUES (MONTH(#sdate));
SET #sdate = date_add(#sdate, INTERVAL 1 MONTH);
ELSE
INSERT INTO tempDates (timeRange) VALUES (DATE(#sdate));
SET #sdate = date_add(#sdate, INTERVAL 1 DAY);
END IF;
END IF;
END WHILE ;
SELECT CASE WHEN r.DateRange IS NULL THEN (#provisioned :=
#provisioned) ELSE (#provisioned := #provisioned + r.Provisioned)
END AS Provisioned,
CASE WHEN r.DateRange IS NULL THEN (#downloaded := #downloaded) ELSE
(#downloaded := #downloaded + r.Downloaded) END AS Downloaded,
CASE WHEN r.DateRange IS NULL THEN d.timeRange ELSE r.DateRange END
AS DateRange FROM (
SELECT sum(result.Provisioned) as Provisioned,
sum(result.Downloaded) AS Downloaded, result.DateRange FROM (
SELECT
1 As Provisioned,
0 AS Downloaded,
CASE WHEN #hourlyReport=TRUE
THEN HOUR(CONVERT_TZ(s.provisioning_date,"+00:00",timezone))
WHEN #monthlyReport=TRUE
THEN MONTH(CONVERT_TZ(s.provisioning_date,"+00:00",timezone))
ELSE DATE(CONVERT_TZ(s.provisioning_date,"+00:00",timezone))
END
AS DateRange
FROM subscription s
INNER JOIN profile_type p ON p.id = s.profile_type
WHERE s.mno_id = mno_id
AND (#excludeProfileFilter=true or p.display_name=profile_type OR p.subscription_type=profile_type)
AND DATE(CONVERT_TZ(s.provisioning_date,"+00:00",timezone)) BETWEEN DATE(CONVERT_TZ(start_date,"+00:00",timezone)) AND DATE(CONVERT_TZ(end_date,"+00:00",timezone))
UNION ALL
SELECT
0 As Provisioned,
1 As Downloaded,
CASE WHEN #hourlyReport=TRUE
THEN COALESCE(HOUR(CONVERT_TZ(r.end_download_date,"+00:00",timezone)),HOUR(CONVERT_TZ(r.last_update,"+00:00",timezone)))
WHEN #monthlyReport=TRUE
THEN COALESCE(MONTH(CONVERT_TZ(r.end_download_date,"+00:00",timezone)),MONTH(CONVERT_TZ(r.last_update,"+00:00",timezone)))
ELSE COALESCE(DATE(CONVERT_TZ(r.end_download_date,"+00:00",timezone)),DATE(CONVERT_TZ(r.last_update,"+00:00",timezone)))
END
AS DateRange
FROM subscription s
INNER JOIN profile_type p ON p.id = s.profile_type
LEFT JOIN rsp_session r ON r.profile_iccid = s.iccid
WHERE s.mno_id = mno_id
AND (#excludeProfileFilter=TRUE OR p.display_name=profile_type OR p.subscription_type=profile_type)
AND COALESCE(DATE(CONVERT_TZ(r.end_download_date,"+00:00",timezone)),DATE(CONVERT_TZ(r.last_update,"+00:00",timezone))) BETWEEN DATE(CONVERT_TZ(start_date,"+00:00",timezone)) AND DATE(CONVERT_TZ(end_date,"+00:00",timezone)) AND s.status IN('INSTALLED','ENABLED','DELETED')
) result GROUP BY result.DateRange
) r RIGHT OUTER JOIN tempDates d ON r.DateRange = d.timeRange;
END;;
DELIMITER ;
The result I am getting in MaroiaDB 10.1.22, which is not the correct one is -
2 0 2017-11-02
5 10 2017-11-03
32 16 2017-11-06
51 34 2017-11-07
64 42 2017-11-08
79 47 2017-11-09
79 48 2017-11-10
102 61 2017-11-13
116 61 2017-11-14
128 68 2017-11-15
145 71 2017-11-16
157 82 2017-11-17
196 95 2017-11-20
254 111 2017-11-21
273 118 2017-11-22
313 134 2017-11-23
323 144 2017-11-24
363 149 2017-11-27
368 152 2017-11-28
371 152 2017-11-29
403 160 2017-11-30
403 160 2017-11-01
403 160 2017-11-04
403 160 2017-11-05
403 160 2017-11-11
403 160 2017-11-12
403 160 2017-11-18
403 160 2017-11-19
403 160 2017-11-25
403 160 2017-11-26
It should be in order.
Can anyone tell what is the wrong? Or its DB issue?
Thanks in advance.

You have no order by in any of your queries. The result set can be in any order, because result sets without an order by (like tables) are unordered sets.
Hence, the database is correct. Your understanding is missing this important fact about SQL.
Add the order by that you want and the result set will be appropriately ordered in any version of the database that you use.

Related

Use a single trigger to insert into multiple tables based on a condition

I have a table named three_current. this tables is inserted with 3 new rows every 1 minutes from another application, so the tables keeps on increasing in rows. These three new rows always have their channel number to be 350, 351, and 352. I want a trigger to insert each of these three rows into three separate tables such that each tables contains data with the same channel number.
The three_current tables is as such:
three_current table
datetime
channel_number
Value
Status
01/06/2021 22:45:00
350
100
1
01/06/2021 22:45:00
351
120
1
01/06/2021 22:45:00
352
110
1
01/06/2021 22:46:00
350
95
1
01/06/2021 22:46:00
351
105
1
01/06/2021 22:46:00
352
150
1
01/06/2021 22:47:00
350
195
1
01/06/2021 22:47:00
351
205
1
01/06/2021 22:47:00
352
250
1
I also have three other tables name red_current, yellow_current, and blue_current. I am trying without success to create a trigger to update these three tables based on the channel_number of three_current table such that
red_current table will be
datetime
channel_number
Value
Status
01/06/2021 22:45:00
350
100
1
01/06/2021 22:46:00
350
95
1
01/06/2021 22:47:00
350
195
1
yellow_current table will be
datetime
channel_number
Value
Status
01/06/2021 22:45:00
351
120
1
01/06/2021 22:46:00
351
105
1
01/06/2021 22:47:00
351
205
1
blue_current table will be
datetime
channel_number
Value
Status
01/06/2021 22:45:00
352
110
1
01/06/2021 22:46:00
352
150
1
01/06/2021 22:47:00
352
250
1
But what I get after executing my code is that the red_current, yellow_current and the blue_current tables are all being inserted with rows where the channel number is 350. This means that only the red_current table is correct while the other two tables are duplicates of the red_current table. (I feel my code can only execute for the first row of each updates received by three_current table and thats the row with channel number 350).
My code is as follows:
DELIMITER //
CREATE TRIGGER `add` AFTER INSERT ON `three_current`
FOR EACH ROW
BEGIN
DECLARE new_datetime datetime ; -- choose the datatypes
DECLARE new_channel_number int; --
DECLARE new_value double; --
DECLARE new_status smallint; --
SET new_datetime = new.datetime ;
SET new_channel_number = new.channel_number ;
SET new_value = new.value ;
SET new_status = new.status;
INSERT INTO red_current (datetime, channel_number, value, status)
SELECT new.datetime, new.channel_number , new.value, new.status
FROM three_current WHERE channel_number = '350'
ON DUPLICATE KEY UPDATE status = new.status;
INSERT INTO yellow_current (datetime, channel_number, value, status)
SELECT new.datetime, new.channel_number , new.value, new.status
FROM three_current WHERE channel_number = '351'
ON DUPLICATE KEY UPDATE status = new.status;
INSERT INTO blue_current (datetime, channel_number, value, status)
SELECT new.datetime, new.channel_number , new.value, new.status
FROM three_current WHERE channel_number = '352'
ON DUPLICATE KEY UPDATE status = new.status ;
END
//
DELIMITER ;

Apply a function for all columns of a table

I am trying to make a calculation in MySQL for all columns of a table.
Table: bev
Jahr GKZ gesamt A B C
2017 1111000 88.519 855 888 814
2017 1112000 247.943 2.414 2.379 2.262
2017 1113000 253.106 2.290 2.343 2.289
2017 1113004 43.392 408 416 403
2017 1113008 12.383 137 134 124
2017 1113012 27.106 252 252 249
2017 1113016 41.673 391 410 398
2017 1113020 39.585 364 391 373
2017 1113024 10.075 63 73 74
2017 1113028 24.083 199 205 209
2017 1113032 8.745 63 77 65
2017 1113036 18.143 170 170 143
2017 1113040 27.921 243 215 251
Table: ja
GKZ Jahr ja_name
1001000 2017 K X
1002000 2017 K Y
5370000 2017 L Z
5370004 2017 Z1
5370012 2017 Z2
5370016 2017 Z3
5370020 2017 Z4
I already got the calculation for one column (the first one: gesamt) in a function:
CREATE DEFINER=`DB`#`%` FUNCTION `Total_Amount_Funct`(
bev_ID int(11),
bev_Total int(11),
ja_name VARCHAR(255),
ja_jahr int(11)) RETURNS int(11)
DETERMINISTIC
BEGIN
DECLARE Total_Amount int(11);
DECLARE kreis int(11);
DECLARE Total_Sum int(11);
SET kreis = (bev_ID / 1000) ;
SET Total_Sum = (SELECT SUM(b.gesamt)
FROM bev as b, ja as j
WHERE b.GKZ = j.GKZ
AND b.Jahr = j.Jahr
AND j.Jahr = ja_jahr
AND (MOD(b.GKZ, 1000) <> 0)
AND (MOD(b.GKZ, 1000) != 0)
AND NOT (MOD(b.GKZ, 1000) = 0)
AND (b.GKZ BETWEEN (kreis*1000 + 1) AND (((kreis+1)*1000)-1))
AND j.ja_name IS NOT NULL);
SET Total_Amount = bev_Total-Total_Sum;
RETURN (Total_Amount);
END
This function can be called with the following select:
SELECT DISTINCT
bev.GKZ,
bev.Jahr,
bev.gesamt,
CASE WHEN (bev.GKZ % 1000 = 0) THEN
coalesce(Total_Amount_Funct(bev.GKZ, bev.gesamt, ja.ja_name, bev.Jahr), bev.gesamt)
ELSE bev.gesamt
END AS bev,
ja.ja_name
FROM
ja, bev
WHERE
bev.GKZ = ja.GKZ
AND bev.Jahr = ja.Jahr;
I really would like to apply the function for all columns of the table. Maybe as a stored procedure? Maybe as dynamic columns. I do not know. I have solved this problem in MS SQL with dynamic columns but I have the feeling that translating it will take more time than trying to complete the function as a Stored Procedure.
The name of the columns can be obtained by:
SELECT column_name
FROM information_schema.columns
WHERE table_name='bev'
and column_name not in ('Jahr','GKZ');
As Result it should be:
GKZ Jahr gesamt bev ja_name
1111000 2017 88.519 88.519 K X
1112000 2017 247.943 247.943 K Y
1113000 2017 253.106 101.350 L Z
1113004 2017 43.392 43.392 Z1
1113012 2017 27.106 27.106 Z2
1113016 2017 41.673 41.673 Z3
1113020 2017 39.585 39.585 Z4
As you are using the column only in the SUM, you could pass the column name as parameter and use CASE-statement to pick the column accordingly. Something like:
CREATE FUNCTION `Total_Amount_Funct`(
bev_ID decimal(8,3),
bev_Total int,
ja_name VARCHAR(255),
ja_jahr int,
in_col varchar(10)
)
RETURNS int
DETERMINISTIC
BEGIN
DECLARE Total_Amount int(11);
DECLARE Total_Sum int(11);
SELECT
SUM(
case
when in_col='gesamt' then b.gesamt
when in_col='A' then b.A
when in_col='B' then b.B
when in_col='C' then b.C
end
) into Total_Sum
FROM bev as b
join ja as j on b.GKZ = j.GKZ AND b.Jahr = j.Jahr
WHERE
MOD(b.GKZ, 1000) != 0
AND b.GKZ BETWEEN bev_ID+1 AND bev_ID+999
AND j.ja_name IS NOT NULL
SET Total_Amount = bev_Total-Total_Sum;
RETURN (Total_Amount);
END
And then call the function with column name and correct value:
Total_Amount_Funct(bev.GKZ, bev.gesamt, ja.ja_name, bev.Jahr, 'gesamt'),
Total_Amount_Funct(bev.GKZ, bev.A, ja.ja_name, bev.Jahr, 'A')
...
Note that calling a function which makes a query will serialize your SQL (calling the function on each row causes the function query to be executed on each row). This will hurt the query performance.
slaakso,
thank you very much for your answer. You are from today my idol :-).
Thanks Thanks.
I have maybe one performance Question.
It is posible to write the function for all columns of the table bev. We can copy the column names in a temporary table:
CREATE TEMPORARY TABLE listColumns(
Columns_ID MEDIUMINT NOT NULL AUTO_INCREMENT ,
Columnsnamen varchar(256) ,
PRIMARY KEY (Columns_ID)
);
Readed from the System Information:
insert into listColumns (Columnsnamen)
SELECT column_name
FROM information_schema.columns
WHERE table_name='bev'
and column_name not in ('Jahr','GKZ');
This Table looks like:
Columns_ID Columnsnamen
1 gesamt
2 A
3 B
4 C
5
6
In such a way, that it is not necessary to mentione every column name (the table contains about 100 columns). Maybe with a cursor over the Columns_ID?
It woul be great if you have another advice for me.
Thank you and kind regads
Ana

Get count of columns having same value in comma separated format Sql

Hi i need a complex query
my table structure is
attribute_id value entity_id
188 48,51,94 1
188 43,22 2
188 43,22 3
188 43,22 6
190 33,11 10
190 90,61 12
190 90,61 15
I need the count of the value like
attribute_id value count
188 48 2
188 43 3
188 51 1
188 94 1
188 22 2
190 33 1
190 11 1
190 90 2
190 61 2
I have searched a lot on google to have something like this but unfortunately i didn't get any success. Please suggest me how can i achieve this .
I use a UDF for things like this. If that could work for you:
CREATE FUNCTION [dbo].[UDF_StringDelimiter]
/*********************************************************
** Takes Parameter "LIST" and transforms it for use **
** to select individual values or ranges of values. **
** **
** EX: 'This,is,a,test' = 'This' 'Is' 'A' 'Test' **
*********************************************************/
(
#LIST VARCHAR(8000)
,#DELIMITER VARCHAR(255)
)
RETURNS #TABLE TABLE
(
[RowID] INT IDENTITY
,[Value] VARCHAR(255)
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE
#LISTLENGTH AS SMALLINT
,#LISTCURSOR AS SMALLINT
,#VALUE AS VARCHAR(255)
;
SELECT
#LISTLENGTH = LEN(#LIST) - LEN(REPLACE(#LIST,#DELIMITER,'')) + 1
,#LISTCURSOR = 1
,#VALUE = ''
;
WHILE #LISTCURSOR <= #LISTLENGTH
BEGIN
INSERT INTO #TABLE (Value)
SELECT
CASE
WHEN #LISTCURSOR < #LISTLENGTH
THEN SUBSTRING(#LIST,1,PATINDEX('%' + #DELIMITER + '%',#LIST) - 1)
ELSE SUBSTRING(#LIST,1,LEN(#LIST))
END
;
SET #LIST = STUFF(#LIST,1,PATINDEX('%' + #DELIMITER + '%',#LIST),'')
;
SET #LISTCURSOR = #LISTCURSOR + 1
;
END
;
RETURN
;
END
;
The UDF takes two parameters: A string to be split, and the delimiter to split by. I've been using it for all sorts of different things over the years, because sometimes you need to split by a comma, sometimes by a space, sometimes by a whole string.
Once you have that UDF, you can just do this:
DECLARE #TABLE TABLE
(
Attribute_ID INT
,Value VARCHAR(55)
,Entity_ID INT
);
INSERT INTO #TABLE VALUES (188, '48,51,94', 1);
INSERT INTO #TABLE VALUES (188, '43,22', 2);
INSERT INTO #TABLE VALUES (188, '43,22', 3);
INSERT INTO #TABLE VALUES (188, '43,22', 6);
INSERT INTO #TABLE VALUES (190, '33,11', 10);
INSERT INTO #TABLE VALUES (190, '90,61', 12);
INSERT INTO #TABLE VALUES (190, '90,61', 15);
SELECT
T1.Attribute_ID
,T2.Value
,COUNT(T2.Value) AS Counter
FROM #TABLE T1
CROSS APPLY dbo.UDF_StringDelimiter(T1.Value,',') T2
GROUP BY T1.Attribute_ID,T2.Value
ORDER BY T1.Attribute_ID ASC, Counter DESC
;
I did an ORDER BY Attribute_ID ascending and then the Counter descending so that you get each Attribute_ID with the most common repeating values first. You could change that, of course.
Returns this:
Attribute_ID Value Counter
-----------------------------------
188 43 3
188 22 3
188 94 1
188 48 1
188 51 1
190 61 2
190 90 2
190 11 1
190 33 1

Update Table with Case stmt

This is a fun side project I have been working on. I added a column for weight (op1w to op12w) to go with the description (op_1 to op_12). Now I need to go back through and update all entries with the appropriate op1w to op12w depending on the op_1 to op_12. Here is a part of the drop_log table with those columns.
Here are the values to match and what to update the op1w to op12w with (this exists as a table called mechs in the db):
mech weight
AS7 100
AWS 80
BJ 45
BLR 85
CDA 40
CN9 50
COM 25
CPL 65
CTF 70
DISC 0
DRG 60
GRF 55
HBK 50
HGN 90
JM6 65
JR7 35
KTO 55
LCT 20
ON1 75
QKD 60
RVN 35
SDR 30
SHD 55
STK 85
TBT 50
TDR 65
VTR 80
WVR 55
Would you guys help me with the case statement for this? I believe it should start something like this:
UPDATE drop_log
SET
op1w =(case when some_case_condition then something else op1w end),
op2w =(case when some_case_condition then something else op2w end)
.
Thanks for your help.
edit: OK after reading the comments, here is my attept
UPDATE drop_log
SET op1w=
CASE
WHEN op_1 = 'AS7'
THEN op1w = 100
WHEN op_1 = 'AWS'
THEN op1w = 80
.
.
.
ELSE op_1
END
CASE
WHEN op_12 = etc
THEN op12w = etc
.
.
.
ELSE op_12
END
Ok now I see what you guys are saying.. No I don't want you to fill it our for me. This is going to go on and on for each column 1-12 and then each column gets its own case for each weight possibility. Is that all there is to it? I thought it would be more involved.
The general format of your query should be:
UPDATE drop_log
SET op1w = CASE
WHEN op_1 = 'AWS' THEN '1'
WHEN op_2 = 'ABC' THEN op1
ELSE '0'
END,
op2w = CASE
WHEN op_1 = 'HDP' THEN op_1
ELSE '0'
END

Get frequency distribution of a decimal range in MySQL

I'm looking for an elegant way (in terms of syntax, not necessarily efficient) to get the frequency distribution of a decimal range.
For example, I have a table with ratings column which can be a negative or positive. I want to get the frequency of rows with a rating of certain range.
- ...
- [-140.00 to -130.00): 5
- [-130.00 to -120.00): 2
- [-120.00 to -110.00): 1
- ...
- [120.00 to 130.00): 17
- and so on.
[i to j) means i inclusive to j exclusive.
Thanks in advance.
You could get pretty close using 'select floor(rating / 10), count(*) from (table) group by 1'
I was thinking of seomthing that could do many levels like
DELIMITER $$
CREATE PROCEDURE populate_stats()
BEGIN
DECLARE range_loop INT Default 500 ;
simple_loop: LOOP
SET the_next = range_loop - 10;
Select sum(case when range between range_loop and the_next then 1 else 0 end) from table,
IF the_next=-500 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
usage: call populate_stats();
Would handle 100 ranges from 500-490, 490-480, ... -480 - -490, -490 - -500
assuming a finite number of ranges.
Select
sum(case when val between -140 to -130 then 1 else 0 end) as sum-140_to_-130,
sum(Case when val between -130 to -120 then 1 else 0 end) as sum-130_to_-140,
...
FROM table
and if not, you could use dynamic SQL to generate the select allowing a number of ranges however you may run into a column limitation.
Just put your desired ranges into a table, and use that to discriminate the values.
-- SET search_path='tmp';
DROP TABLE measurements;
CREATE TABLE measurements
( zval INTEGER NOT NULL PRIMARY KEY
);
INSERT INTO measurements (zval)
SELECT generate_series(1,1000);
DELETE FROM measurements WHERE random() < 0.20 ;
DROP TABLE ranges;
CREATE TABLE ranges
( zmin INTEGER NOT NULL PRIMARY KEY
, zmax INTEGER NOT NULL
);
INSERT INTO ranges(zmin,zmax) VALUES
(0, 100), (100, 200), (200, 300), (300, 400), (400, 500),
(500, 600), (600, 700), (700, 800), (800, 900), (900, 1000)
;
SELECT ra.zmin,ra.zmax
, COUNT(*) AS zcount
FROM ranges ra
JOIN measurements me
ON me.zval >= ra.zmin AND me.zval < ra.zmax
GROUP BY ra.zmin,ra.zmax
ORDER BY ra.zmin
;
Results:
zmin | zmax | zcount
------+------+--------
0 | 100 | 89
100 | 200 | 76
200 | 300 | 76
300 | 400 | 74
400 | 500 | 86
500 | 600 | 78
600 | 700 | 75
700 | 800 | 75
800 | 900 | 80
900 | 1000 | 82
(10 rows)