mysql explain result interpretation - mysql

The query below does exactly what I expect it to do, is intuitive and doesn't generate intermediary tables. The downside is that it takes a long time to complete.
What I'll do in such cases is break the query in steps and create those intermediary tables & indexes. This time around I'd like to get a better handle on the hints provided by explain, and would appreciate any pointers: what obvious optimization steps am I missing in the query below?
Following the advice in MySQL query optimization and EXPLAIN for a noob I've created indices on order_number , order_type and item in orders_raw. It's unclear however how these would carry over character processing/regexes.
SELECT bundle_headers.order_number , bundle_headers.title , digital_subs.subscription_id , 1 as bundle_component
from
(
select order_number , substring( item , 1 , 3 ) as title , quantity from orders_raw
where order_type in (4,6)
) bundle_headers
inner join
(
select order_number , subscription_id , item as title , quantity from orders_raw
where order_type = 0 and length( item ) = 4
) digital_subs
on bundle_headers.order_number = digital_subs.order_number and
digital_subs.title regexp concat( '.*' , bundle_headers.title , '.*' ) and
bundle_headers.quantity = digital_subs.quantity
UNION
SELECT bundle_headers.order_number , bundle_headers.title , print_subs.subscription_id , 1 as bundle_component
from
(
select order_number , substring( item , 1 , 3 ) as title , quantity from orders_raw
where order_type in (4,6)
) bundle_headers
inner join
(
select order_number , subscription_id , item as title , quantity from orders_raw
where order_type = 0 and length( item ) = 3
) print_subs
on bundle_headers.order_number = print_subs.order_number and
print_subs.title regexp concat( '.*' , bundle_headers.title , '.*' ) and
bundle_headers.quantity = print_subs.quantity;
EDIT, #tin tran: I've yet to rigorously time both the query above and your query (after a couple corrections, copied below) starting out on an idle machine. I did submit it, and didn't see an obvious reduction in run time.
SELECT bundle_headers.order_number,
substring(bundle_headers.item,1,3) as title,
subs.subscription_id,
1 as bundle_component
FROM orders_raw bundle_headers
INNER JOIN orders_raw subs ON (bundle_headers.order_number = subs.order_number)
WHERE (bundle_headers.order_type = 4 OR bundle_headers.order_type = 6)
AND subs.order_type = 0
AND bundle_headers.quantity = subs.quantity
AND subs.item LIKE CONCAT('%',substring(bundle_headers.item,1,3),'%')
AND (length(subs.item) = 4 OR length(subs.item) = 3)

please try this query see if it produces the same result. And if it's any faster
SELECT bundle_headers.order_number,substring(bundle_headers.title,1,3) as title,subs.subscription_id,1 as bundle_component
FROM order_type bundle_headers
INNER JOIN orders_raw subs ON (bundle_headers.order_number = subs.order_number)
WHERE (bundle_headers.order_type = 4 OR bundle_headers.order_type = 6)
AND subs.order_type = 0
AND bundle_headers.quantity = subs.quantity
AND subs.title LIKE CONCAT('%',substring(bundle_headers.title,1,3),'%')
AND (length(subs.item) = 4 OR length(subs.item) = 3)

Related

like is not working in mySql sub query

I have used some sub query , its not working at all ,if i am giving hardcoded id its working .
SELECT
(
SELECT
COUNT(*)
FROM
inventory_set_variations isv
WHERE
isv.c_catid LIKE '%[{"id":"c1.catid"}]%' AND isv.company_id = 1
) AS 'count_total_no_of_variations',
`c1`.`catid` AS `catid`
FROM
category c1
WHERE
(
`c1`.`catsid` <> 3 AND c1.company_id = '1'
)
this is not working ,
below code is working as i gave hard coded data
SELECT
(
SELECT
COUNT(*)
FROM
inventory_set_variations isv
WHERE
isv.c_catid LIKE '%[{"id":"1000020"}]%' AND isv.company_id = 1
) AS 'count_total_no_of_variations',
`c1`.`catid` AS `catid`
FROM
category c1
WHERE
(
`c1`.`catsid` <> 3 AND c1.company_id = '1'
)
You can use CONCAT to form like condition
Change
LIKE '%[{"id":"c1.catid"}]%'
to
LIKE CONCAT('%[{"id":"',c1.catid,'"}]%')

Sql query with three conditions

I have a database with a table having content as below :
message_number message_type message_chat
0 IN Hi
1 OB Hello
2 IN Help
3 IN Want to find this thing
4 OB Sure
5 OB Please let me know
I have written 5 rows since i want to incorporate all possible cases that i want in my query in the example table that i showed.
Now in my query output, i want something like :
message_in message_out
Hi Hello
Help NULL
Want to find this string Sure
NULL Please let me know
So the cases that i want to consider are :
suppose if message_number=0 and message_number=1 both have message_type value as IN then put message_chat_in as message_chat(at message_number=0) and message_chat out as NULL and the iterate over message_number=1
if message_number =0 have message_type=IN and message_number =1 have message_type=OB, then show message_chat(at message_number=0) as message_chat_in and message_chat(at message_number=1) as message_out and dont iterate over message_number=1;
hope i have clarified the condition though i have included all three condition in the expected output.How should my sqlquery look like?
Edit : I am using mysql version 5.5.8
Try the following query
SELECT
q1.message_number in_num,
q1.message_chat in_chat,
q2.message_number out_num,
q2.message_chat out_chat
FROM
(
SELECT *,#i1:=IFNULL(#i1,0)+1 num
FROM Chat
ORDER BY message_number
) q1
LEFT JOIN
(
SELECT *,#i2:=IFNULL(#i2,0)+1 num
FROM Chat
ORDER BY message_number
) q2
ON q2.num=q1.num+1 AND q2.message_type<>q1.message_type
WHERE q1.message_type='IN'
UNION ALL
SELECT
q1.message_number in_num,
q1.message_chat in_chat,
q2.message_number out_num,
q2.message_chat out_chat
FROM
(
SELECT *,#i3:=IFNULL(#i3,0)+1 num
FROM Chat
ORDER BY message_number
) q1
RIGHT JOIN
(
SELECT *,#i4:=IFNULL(#i4,0)+1 num
FROM Chat
ORDER BY message_number
) q2
ON q2.num=q1.num+1 AND q2.message_type<>q1.message_type
WHERE q2.message_type='OB'
AND q1.message_type IS NULL
ORDER BY IFNULL(in_num,out_num)
SQL Fiddle - http://sqlfiddle.com/#!9/95a515/1
The second variant
SET #i1 = 0;
SET #i2 = 0;
SET #i3 = 0;
SET #i4 = 0;
-- the same query
SQL Fiddle - http://sqlfiddle.com/#!9/95a515/2
Or
SELECT 0,0,0,0 INTO #i1,#i2,#i3,#i4;
-- the same query
SQL Fiddle - http://sqlfiddle.com/#!9/95a515/5
why not using a analytic function here? I would do it with Lead() like this:
with inc as (
--Do the incorporation in this block. could be subquery too
--but its easier to read this way.
select
case when message_type = 'IN'
then message_chat
end as message_in
,case when LEAD(message_type) OVER (Order by message_number) = 'OB' --get the next message by number if it is type OB
then LEAD(message_chat) OVER (order by message_number)
end as message_out
from input
)
select *
from inc
where coalesce(message_in, message_out) is not null --filter out rows where with in & out is null
Ok, since there is no analytical functions in MySQL less than 8 the code may not be easy to follow:
with data_rn as
(
-- this isolate consecutive rows with the same message_type
select d1.*, count(d2.message_number) rn
from data d1
left join data d2 on d1.message_number > d2.message_number and d1.message_type != d2.message_type
group by d1.message_number
),
data_rn2 as
(
-- this marks the rows where new rows has to be added (i.e. when rn2 != 0)
select d1.*, count(d2.message_number) rn2
from data_rn d1
left join data_rn d2 on d1.rn = d2.rn and d1.message_type = d2.message_type and d1.message_number > d2.message_number
group by d1.message_number
),
data_added as
(
-- this add new rows
select message_number, message_type, message_chat
from data_rn2
union all
select message_number - 0.5, 'OB', NULL from data_rn2 where message_type = 'IN' and rn2 != 0
union all
select message_number - 0.5, 'IN', NULL from data_rn2 where message_type = 'OB' and rn2 != 0
order by message_number
), data_added_rn as
(
-- this compute new row numbering
select d1.*, ceil((count(d2.message_number)+1)/2) rn
from data_added d1
left join data_added d2 on d1.message_number > d2.message_number
group by d1.message_number
)
-- this will do the final formating
select max(case when message_type = 'IN' then message_chat end) message_in,
max(case when message_type = 'OB' then message_chat end) message_out
from data_added_rn
group by rn
demo
I have tried to comment each section appropriately.

Combining 2 Tables with an OUTER JOIN on Another Table

I need to combine 2 tables that may or may not have the data in them, but than I need a full outer join where the last table (if has content where IsActive = 1) gets shown that data, instead of the combined first 2 tables.
Currently have this:
( SELECT qp.ItemName AS name
, qp.TimeAdded AS created
, '' AS effective
, qp.VendorName AS supplier
, qp.Source AS source
, qp.VendorType AS type
, qp.Price AS cost
, '' AS price
, '' AS markup
, '' AS customer
, '' AS customerListID
, qp.VendorListID AS vendorListID
, '' AS itemListID
FROM wp_quantum_purchases AS qp
WHERE qp.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qp.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT qs.ItemName AS name
, qs.TimeAdded AS created
, qs.SalesDate AS effective
, '' AS supplier
, qs.Source AS source
, '' AS type
, '' AS cost
, qs.Price AS price
, '' AS markup
, qs.CustomerName AS customer
, qs.CustomerListID AS customerListID
, '' AS vendorListID
, '' AS itemListID
FROM wp_quantum_sales AS qs
WHERE qs.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qs.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT hqp.ItemName AS name
, hq.Quote_Date AS created
, hqp.SalesDate AS effective
, hqp.VendorName AS supplier
, hqp.Source AS source
, hqp.VendorType AS type
, hqp.Cost AS cost
, hqp.Price AS price
, CAST(( ( ( CAST(hqp.Price AS DECIMAL(10, 2)) - CAST(hqp.Cost AS DECIMAL(10, 2)) ) / CAST(hqp.Cost AS DECIMAL(10, 2)) ) * 100 ) AS DECIMAL(10, 2)) AS markup
, IFNULL(hq.Customer_FullName, 'N/A') AS customer
, hq.Customer_ListID AS customerListID
, hqp.VendorListID AS vendorListID
, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq
ON ( hq.id = hqp.QuoteID )
WHERE hqp.IsActive = 1)
ORDER BY NAME ASC;
But this is duplicating the data in 1st and 2nd tables and shows the data twice. I need the data from 1st and 2nd tables to be combined as 1 (if exists), but to prioritize the last table (wp_hunter_quote_parts) in here as the content to show from, if IsActive = 1 exists in the last table (wp_hunter_quote_parts). However, if IsActive = 1 does not exist in wp_hunter_quote_parts for ItemName than I would like to combine both wp_quantum_purchases and wp_quantum_sales as if it were 1 row.
Can not do a LEFT JOIN since data could exist in wp_quantum_purchases, but not in wp_quantum_sales OR data could exist in wp_quantum_sales and not in wp_quantum_purchases, OR data could not exist in either of these, and only exist in wp_hunter_quote_parts as well as data might not even exist in wp_hunter_quote_parts.
So, basically, if ItemName exists in wp_quantum_purchases AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get purchase data from wp_quantum_purchases, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
If ItemName exists in wp_quantum_sales AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get sales data from wp_quantum_sales, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
How can I combine first and second table, than do an outer join on it with another table?
Another Attempt here:
(SELECT IFNULL(qp.ItemName, qs.ItemName) AS name, IFNULL(qp.TimeAdded, qs.TimeAdded) AS created, qs.SalesDate AS effective, qp.VendorName AS supplier, qp.Source AS source, qp.VendorType AS type, qp.Price AS cost, qs.Price AS price, CAST((((CAST(qs.Price AS DECIMAL(10,2)) - CAST(qp.Price AS DECIMAL(10,2))) / CAST(qp.Price AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, qs.CustomerName AS customer, qs.CustomerListID AS customerListID, qp.VendorListID AS vendorListID, '' AS itemListID
FROM wp_quantum_purchases AS qp, wp_quantum_sales AS qs
WHERE (qp.IsActive = 1 OR qs.IsActive = 1)
AND NOT EXISTS (
SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE (qp.ItemName = hqp.ItemName || qs.ItemName = hqp.ItemName) AND hqp.IsActive = 1
)
)
UNION ALL
(SELECT hqp.ItemName AS name, hq.Quote_Date AS created, hqp.SalesDate AS effective, hqp.VendorName AS supplier, hqp.Source AS source, hqp.VendorType AS type, hqp.Cost AS cost, hqp.Price AS price, CAST((((CAST(hqp.Price AS DECIMAL(10,2)) - CAST(hqp.Cost AS DECIMAL(10,2))) / CAST(hqp.Cost AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, IFNULL(hq.Customer_FullName, 'N/A') AS customer, hq.Customer_ListID AS customerListID, hqp.VendorListID AS vendorListID, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq ON (hq.id = hqp.QuoteID)
WHERE (hqp.IsActive = 1))
ORDER BY name ASC
Figured this one would work, but seems that it just keeps going and going and going, and doesn't seem to ever finish the query. No errors that I can see, but doesn't finish ever... And these tables are very small, that is odd...
I may not be understanding your question fully, but you could create a view of the first two tables and then do an outer join with the third table.

JOIN nested subquery returning NULL while updating calculated value

I'm trying to work around the "You can't specify target table for update in FROM clause" MySQL error, which means I've got a nested subquery (temp table). Note, I'm trying to get a SELECT to work before I move on to the actual UPDATE.
What I've got is a ledger table where I'm trying to find the associated row's calculated unit total using active_units (what the row started with) and with each additional adjustment in active units (there is one payment row and multiple adjustment rows in the same table that are associated by commission ID and schedule number). These are grouped by month_num. So if it's active_units = 18 and there are three adjustment rows, one with active_units_chg = -2, then I should end up with 18 + -2 = 16.
When I do this:
SELECT
active_units
, active_units_chg
, active_units_total
, CRM_commission_payments.active_units + (
SELECT SUM(active_units_chg)
FROM CRM_commission_payments AS cp
WHERE cp.CRM_commissions_item_id = CRM_commission_payments.CRM_commissions_item_id
AND cp.schedule_a_no = CRM_commission_payments.schedule_a_no
AND cp.month_num = CRM_commission_payments.month_num
AND cp.item_active = 1
) AS active_units_cal1
FROM CRM_commission_payments
WHERE CRM_commission_payments.payment_type = 'payment'
AND CRM_commission_payments.CRM_quotes_item_id = 2457
active_units_cal1 is correct for the row. However, when I do this with a nested JOIN'd subquery, I get NULL for active_units_calc.calc_chg:
SELECT
active_units
, active_units_chg
, active_units_total
, CRM_commission_payments.active_units + (
SELECT SUM(active_units_chg)
FROM CRM_commission_payments AS cp
WHERE cp.CRM_commissions_item_id = CRM_commission_payments.CRM_commissions_item_id
AND cp.schedule_a_no = CRM_commission_payments.schedule_a_no
AND cp.month_num = CRM_commission_payments.month_num
AND cp.item_active = 1
) AS active_units_cal1
, active_units_calc.calc_chg
FROM CRM_commission_payments
LEFT JOIN (
SELECT
source.calc_chg
, source.CRM_commissions_item_id
, source.schedule_a_no
, source.month_num
FROM CRM_commission_payments AS cp1
INNER JOIN (
SELECT
SUM(active_units_chg) AS calc_chg
, cp.CRM_commissions_item_id
, cp.schedule_a_no
, cp.month_num
FROM CRM_commission_payments AS cp
WHERE cp.item_active = 1
) AS source
WHERE source.CRM_commissions_item_id = cp1.CRM_commissions_item_id
AND source.schedule_a_no = cp1.schedule_a_no
AND source.month_num = cp1.month_num
) AS active_units_calc ON (
active_units_calc.CRM_commissions_item_id = CRM_commission_payments.CRM_commissions_item_id
AND active_units_calc.schedule_a_no = CRM_commission_payments.schedule_a_no
AND active_units_calc.month_num = CRM_commission_payments.month_num
)
WHERE CRM_commission_payments.payment_type = 'payment'
AND CRM_commission_payments.CRM_quotes_item_id = 2457
What am I doing wrong?

Join on max(T.<column>) including further information of T

I have two tables
create table item( id int )
insert into item ( id ) values ( 1 ), ( 2 ), ( 3 )
create table itemstatus
(
itemid int
, ts datetime
, "status" int
)
insert into itemstatus ( itemid, ts, status ) values
( 1, '2013-12-01T12:00:00.000', 1 ),
( 1, '2013-12-01T11:00:00.000', 2 ),
( 1, '2014-01-01T12:00:00.000', 1 ),
( 2, '2011-01-01T12:00:00.000', 1 )
I'd like to get all items with the last status set, in this case
1, '2014-01-01T12:00:00.000', 1
2, '2011-01-01T12:00:00.000', 1
3, NULL, NULL
What's the most efficient way to solve this?
I tried with a subselect and I get the latest timestamp, but I'm not able to add the status since this field is not included in aggregate-function or group-by. If I add it, the results got grouped by status - logically - but that leads to the fact, that I get too much result-lines and would have to add a further condition / subselect.
You may use the Fiddle-link for created tables and testdata. The second query includes the status-field.
Edit:
adding a further join does the trick, but I doubt that's the way to do it.
select
i.*
, d.*
, s.status
from
item i
left join ( select ts = max(ts), itemid from itemstatus group by itemid ) d
on 1 = 1
and i.id = d.itemid
left join itemstatus s
on 1 = 1
and s.itemid = d.itemid
and s.ts = d.ts
See SQL-fiddle for testing.
You can use row_number partitioned by itemid and ordered by ts desc to get the latest registration in itemstatus per itemid.
select I.id,
S.ts,
S.status
from item as I
left outer join (
select S.status,
S.ts,
S.itemid,
row_number() over(partition by S.itemid
order by S.ts desc) as rn
from itemstatus as S
) as S
on I.id = S.itemid and
S.rn = 1