What is the execution order of the associative sub-query SQL? - mysql

I came across this SQL at work, This was written by my colleague. Although there are better solutions, I’m just curious,and now I have simplified it as follows:
-- Calculate the total of the 'APPROVING' salary and the 'AGENT' salary already actual paid
SELECT ifnull(sum(l.salary),0) +
(SELECT ifnull(sum(l1.salary),0)
FROM salary_header h1 JOIN salary_lines l1
ON h1.salary_id = l1.salary_id
WHERE h1.status='APPROVING' AND l1.project_id = l.project_id)
FROM salary_pay_headers h JOIN salary_pay_lines l
ON h.salary_pay_id = l.salary_pay_id
WHERE h.pay_type='AGENT'
AND l.project_id=9904
mysql> select * from salary_header;
+-----------+-----------+
| salary_id | status |
+-----------+-----------+
| 1 | APPROVING |
| 2 | PAID |
+-----------+-----------+
mysql> select * from salary_lines;
+----------------+-----------+------------+--------+
| salary_line_id | salary_id | project_id | salary |
+----------------+-----------+------------+--------+
| 1 | 2 | 9905 | 200.00 |
+----------------+-----------+------------+--------+
mysql> select * from salary_pay_headers;
+---------------+----------+
| salary_pay_id | pay_type |
+---------------+----------+
| 1 | AGENT |
| 2 | OTHER |
+---------------+----------+
mysql> select * from salary_pay_lines;
+--------------------+---------------+------------+--------+
| salary_pay_line_id | salary_pay_id | project_id | salary |
+--------------------+---------------+------------+--------+
| 1 | 1 | 9904 | 3.05 |
| 2 | 1 | 9904 | 201.37 |
| 3 | 1 | 9904 | 6.10 |
| 4 | 1 | 9904 | 10.17 |
| 5 | 1 | 9904 | 6.44 |
| 6 | 1 | 9904 | 9.15 |
| 8 | 3 | 9905 | 100.00 |
+--------------------+---------------+------------+--------+
Its result is not 3.05+201.37+6.10+10.17+6.44+9.15=236.28 as I expected,but 236.28+200=436.28,obviously that one in the salary_line is not filtered out. I have spent the whole afternoon on this problem, so I really want to know the execution order of this SQL.

Related

mysql fill missing values with 0

I have a mysql query that sometimes results in missing values. For my dashboard I'd like to fill those values, but would prefer to avoid build dummy tables if I can.
query:
SELECT COUNT(Comms_Timestamp) as call_count,DAYOFWEEK(Comms_Timestamp) as bucket
FROM tblTest GROUP BY bucket;
results in
+------------+--------+
| call_count | bucket |
+------------+--------+
| 4 | 1 |
| 7 | 2 |
| 7 | 3 |
| 1 | 5 |
| 6 | 6 |
| 1 | 7 |
+------------+--------+
In the above example you can see bucket 4 is missing. I consider the method where the join is to a select union array, however since both fields are aggregates, I'm not sure how to go about it.
test data is
+---------------------+
| Comms_Timestamp |
+---------------------+
| 2018-12-24 06:04:05 |
| 2018-12-24 12:18:39 |
| 2018-12-21 04:24:31 |
| 2018-12-21 08:32:44 |
| 2018-12-30 01:41:06 |
| 2018-12-30 01:53:00 |
| 2018-12-30 01:53:39 |
| 2018-12-30 02:00:01 |
| 2018-12-17 15:55:03 |
| 2018-12-17 16:04:12 |
| 2018-12-17 16:05:41 |
| 2018-12-17 16:07:43 |
| 2018-12-17 16:10:25 |
| 2018-12-18 14:03:22 |
| 2018-12-18 14:03:29 |
| 2018-12-18 14:10:19 |
| 2018-12-18 14:10:29 |
| 2018-12-18 14:10:31 |
| 2018-12-18 14:10:47 |
| 2018-12-18 14:10:55 |
| 2018-12-20 08:21:07 |
| 2018-12-28 11:03:59 |
| 2018-12-28 12:06:40 |
| 2018-12-28 12:15:01 |
| 2018-12-28 14:29:24 |
| 2019-01-05 13:33:43 |
+---------------------+
Since you are using mysql and don't have access to the seq_ option, here is an alternative way:
SELECT A.x AS bucket, IF(ISNULL(COUNT(t2.Comms_Timestamp)), 0, COUNT(t2.Comms_Timestamp)) AS call_count FROM
(select 1 x union select 2 union select 3 union select 4 union select 5 union select 6 union select 7) AS A
LEFT JOIN tblTest AS t2 ON DAYOFWEEK(t2.Comms_Timestamp) = A.x
GROUP BY bucket
ORDER BY bucket;
It may not be the prettiest option but will do what you need.
Here is a db fiddel link: db<>fiddle
If you are using MariaDB there is their Sequence Storage Engine
There is no create needed for this table, however the maximum value must be known.
select version();
| version() |
| :------------------------------------------ |
| 10.3.11-MariaDB-1:10.3.11+maria~stretch-log |
create table bob (a int)
✓
insert into bob values (4),(2)
✓
select * from seq_1_to_5
| seq |
| --: |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
SELECT s.seq, bob.a
FROM seq_1_to_5 s
LEFT JOIN bob
ON bob.a = s.seq
ORDER BY s.seq
seq | a
--: | ---:
1 | null
2 | 2
3 | null
4 | 4
5 | null
db<>fiddle here
You can use IFNULL() function in MYSQL:
SELECT IFNULL(COUNT(C.Comms_Timestamp),0) as call_count,IFNULL(DAYOFWEEK(C.Comms_Timestamp),0) as bucket
FROM tblCommunication as C LEFT JOIN tblCareTeam as CT on C.id_Case = CT.id_Case
GROUP BY CT.id_Site,bucket
HAVING CT.id_Site=8;

Group condition in last data mysql

I have a data like this :
Table LOT
+-------+--------+
|Lot_id | Prod_id|
+-------+--------+
| LOT-1 | Prd-1 |
| LOT-1 | Prd-2 |
| LOT-1 | Prd-3 |
| LOT-2 | Prd-4 |
+-------+--------+
Table Process
+-------+--------+--------+------------+----------+
|proc_id|proc_cat|proc_seq|proc_prod_id|t_proc_qty|
+-------+--------+--------+------------+----------+
| 1 | Proc-A | 1 | Prd-1 | 100 |
| 2 | Proc-H | 2 | Prd-1 | 100 |
| 3 | Proc-D | 3 | Prd-1 | 100 |
| 4 | Proc-A | 1 | Prd-2 | 100 |
| 5 | Proc-H | 2 | Prd-2 | 100 |
| 6 | Proc-D | 3 | Prd-2 | 20 |
| 7 | Proc-Q | 4 | Prd-2 | 20 |
| 8 | Proc-A | 1 | Prd-3 | 100 |
| 9 | Proc-H | 2 | Prd-3 | 100 |
| 10 | Proc-D | 3 | Prd-3 | 50 |
| 11 | Proc-O | 1 | Prd-4 | 80 |
| 12 | Proc-F | 2 | Prd-4 | 80 |
| 13 | Proc-H | 3 | Prd-4 | 80 |
+-------+--------+--------+------------+----------+
And i want data like this if i want select just LOT=LOT-1.
table LOT joined to table Process and data is accumulated sum(t_proc_qty) from last proc_seq each proc_prod_id and group by proc_cat and order by proc_seq
+--------+--------+----------+
|proc_cat|proc_seq|t_proc_qty|
+--------+--------+----------+
| Proc-D | 3 | 150 |->accumulated from Prd-1 and prd-3 in last process is seq 3
| Proc-Q | 4 | 20 |->accumulated from Prd-2 in last process is seq 4
+--------+--------+----------+
What queries I use in MySQL ?
I stucked in query
SELECT proc_cat, proc_seq, SUM(t_proc_qty)
FROM Process
LEFT JOIN Lot ON proc_prod_id=Prod_id
WHERE Lot_id='LOT-1'
GROUP BY proc_prod_id
ORDER BY proc_seq DESC LIMIT 1
this schema for trial query SQLFiddle
From table Process you want the records with the highest proc_id per proc_prod_id:
select *
from process
where not exists
(
select *
from process later
where later.proc_prod_id = process.proc_prod_id
and later.proc_id > process.proc_id
);
From this data you want an aggregate per proc_cat and proc_sec. And you also want to consider only prod_id for 'LOT-1' in table LOT.
The complete query:
select proc_cat, proc_seq, sum(t_proc_qty)
from process
where proc_prod_id in (select prod_id from lot where lot_id = 'LOT-1')
and not exists
(
select *
from process later
where later.proc_prod_id = process.proc_prod_id
and later.proc_id > process.proc_id
)
group by proc_cat, proc_seq
order by proc_cat, proc_seq;
SQL fiddle: http://sqlfiddle.com/#!9/1fa3fd/5

MySQL Join Query without duplicate values

invoice table
SELECT id, fname, gtotal, `date` FROM invoice WHERE id = 1;
| id | fname | gtotal | date |
|----|---------|--------|-----------------------|
| 1 | Brandon | 860 | May, 11 2016 00:00:00 |
invoice_contents table,
SELECT * FROM invoice_contents WHERE invoice_id = 1;
| id | invoice_id | item | price | quantity | discount | total |
|----|------------|------------|-------|----------|----------|-------|
| 1 | 1 | Dextrose | 10 | 10 | 5 | 95 |
| 2 | 1 | Nescaine | 20 | 30 | 10 | 540 |
| 3 | 1 | Anticavity | 30 | 10 | 25 | 225 |
This JOIN query
SELECT invoice.id, invoice.fname, invoice_contents.item,
invoice_contents.price, invoice_contents.quantit,
invoice_contents.discount, invoice_contents.total,
invoice.gtotal
FROM invoice_contents
INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1;
gives this result.
| id | fname | item | price | quantity | discount | total | gtotal |
|----|---------|------------|-------|----------|----------|-------|--------|
| 1 | Brandon | Dextrose | 10 | 10 | 5 | 95 | 860 |
| 1 | Brandon | Nescaine | 20 | 30 | 10 | 540 | 860 |
| 1 | Brandon | Anticavity | 30 | 10 | 25 | 225 | 860 |
I need this result.
| id | fname | item | price | quantity | discount | total | gtotal |
|----|---------|------------|-------|----------|----------|-------|--------|
| 1 | Brandon | Dextrose | 10 | 10 | 5 | 95 | 860 |
| | | Nescaine | 20 | 30 | 10 | 540 | |
| | | Anticavity | 30 | 10 | 25 | 225 | |
I am just a beginner in MySQL. I have been trying from this morning to get this kind of output by experimenting on different combinations please help me out.
#Rex, Your select is correct. You should make desired output using some script e.g. PHP.
try this in SQL:
in this Query i save everytime fname in a variable is not equal and at the next row i compare it and return a empty string is it equal. and the same for gtotal.
the cross join is only to initialize the variables.
in this case it is important that the rows are order by fname to ensure that the same name is behind each other
SELECT
invoice.id,
IF(#last_fname = invoice.fname, '', (#last_fname:=invoice.fname)) as fname,
invoice_contents.item,
invoice_contents.price,
invoice_contents.quantity,
invoice_contents.discount,
IF(#last_gtotal = invoice.gtotal, '', (#last_gtotal:=invoice.gtotal)) as gtotal
FROM invoice_contents
INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1
CROSS JOIN ( select #last_fname := '' , #last_gtotal := '' ) AS parameter
ORDER BY invoice.fname;
Sample
MariaDB [bb]> SELECT
-> invoice.id,
-> IF(#last_fname = invoice.fname, '', (#last_fname:=invoice.fname)) AS fname,
-> invoice_contents.item,
-> invoice_contents.price,
-> invoice_contents.quantity,
-> invoice_contents.discount,
-> IF(#last_gtotal = invoice.gtotal, '', (#last_gtotal:=invoice.gtotal)) AS gtotal
-> FROM invoice_contents
-> INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1
-> CROSS JOIN ( SELECT #last_fname:='' , #last_gtotal:='' ) AS parameter
-> ORDER BY invoice.fname;
+----+---------+------------+-------+----------+----------+--------+
| id | fname | item | price | quantity | discount | gtotal |
+----+---------+------------+-------+----------+----------+--------+
| 1 | Brandon | Dextrose | 10.00 | 10 | 5.00 | 860.00 |
| 1 | | Nescaine | 20.00 | 30 | 10.00 | |
| 1 | | Anticavity | 30.00 | 10 | 25.00 | |
+----+---------+------------+-------+----------+----------+--------+
3 rows in set, 1 warning (0.00 sec)
MariaDB [bb]>

Finding average of the column generated from sql query having group by function

I want to find the average of the following data via mysql query (assume these are 719 rows).
| 1 |
| 3 |
| 1 |
| 2 |
| 2 |
| 1 |
| 1 |
| 2 |
| 1 |
| 1 |
| 2 |
| 1 |
| 2 |
| 1 |
| 2 |
| 1 |
| 1 |
+----------+
719 rows in set (2.43 sec)
SELECT COUNT(*) FROM osdial_agent_log WHERE DATE(event_time)='2015-11-01' GROUP BY lead_id;
I ran this query to get that data
Can someone help me to find the average for the above data.
Use
SELECT AVG(total)
FROM (SELECT COUNT(*) AS total
FROM osdial_agent_log
WHERE DATE(event_time)='2015-11-01'
GROUP BY lead_id) t

mysql: comparing two columns

my tables and their layout:
mysql> select * FROM xt_shipping_zones;
+---------+-------------+---------------------------------------------------------------------------+
| zone_id | zone_name | zone_countries |
+---------+-------------+---------------------------------------------------------------------------+
| 5 | ZONE1 | AT,BE,BG,DK,FI,FR,GR,IE,IT,LV,LT,LU,MC,NL,PL,PT,RO,SM,SE,SK,SI,ES,HU,GB |
| 6 | Deutschland | DE |
| 8 | ZONE2Brutto | AD,NO,VA |
| 9 | ZONE2NETTO | CH,LI |
+---------+-------------+---------------------------------------------------------------------------+
mysql> select * FROM xt_shipping_cost WHERE shipping_geo_zone = 99995 LIMIT 5;
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
| shipping_cost_id | shipping_id | shipping_geo_zone | shipping_country_code | shipping_type_value_from | shipping_type_value_to | shipping_price | shipping_allowed |
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
| 269 | 34 | 99995 | | 0.31 | 17.99 | 17.0000 | 1 |
| 270 | 34 | 99995 | | 17.99 | 35.99 | 34.0000 | 1 |
| 271 | 34 | 99995 | | 35.99 | 53.99 | 51.0000 | 1 |
| 272 | 34 | 99995 | | 53.99 | 71.99 | 68.0000 | 1 |
| 273 | 34 | 99995 | | 71.99 | 89.99 | 85.0000 | 1 |
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
mysql> SELECT * FROM geoip WHERE 92569600 BETWEEN start AND end;
+----------+----------+---------+-----+
| start | end | country | id |
+----------+----------+---------+-----+
| 92569600 | 92585983 | AT | 895 |
+----------+----------+---------+-----+
My Query:
SELECT
xt_shipping_cost.shipping_type_value_from,
xt_shipping_cost.shipping_type_value_to,
xt_shipping_cost.shipping_price,
geoip.country
FROM xt_shipping_cost
INNER JOIN xt_shipping_zones
ON xt_shipping_cost.shipping_geo_zone = xt_shipping_zones.zone_id + 99990
INNER JOIN geoip
ON geoip.country REGEXP xt_shipping_zones.zone_countries
WHERE 34664448 BETWEEN geoip.start AND geoip.end
My Problem:
Query is working if there is only ONE entry in xt_shipping_zones.zone_countries like DE. If there are multiple (with comma seperated entries) i cant get a match on that row.
Doing it manually:
mysql> SELECT * FROM `xt_shipping_zones` WHERE `zone_countries` REGEXP 'AT';
+---------+-----------+---------------------------------------------------------------------------+
| zone_id | zone_name | zone_countries |
+---------+-----------+---------------------------------------------------------------------------+
| 5 | ZONE1 | AT,BE,BG,DK,FI,FR,GR,IE,IT,LV,LT,LU,MC,NL,PL,PT,RO,SM,SE,SK,SI,ES,HU,GB |
+---------+-----------+---------------------------------------------------------------------------+
SQLFiddle: http://sqlfiddle.com/#!9/68f8d0/1
I hope i didn't failed to much to make my problem clear.
Thank you
I think you can use find_in_set()
SELECT
xt_shipping_cost.shipping_type_value_from,
xt_shipping_cost.shipping_type_value_to,
xt_shipping_cost.shipping_price,
geoip.country
FROM xt_shipping_cost
INNER JOIN xt_shipping_zones
ON xt_shipping_cost.shipping_geo_zone = xt_shipping_zones.zone_id + 99990
INNER JOIN geoip
ON find_in_set(geoip.country, xt_shipping_zones.zone_countries)
WHERE 34664448 BETWEEN geoip.start AND geoip.end
It is no good idea to store the values as csv. That is very bad database design.