mysql table views with conditions - mysql

I have an online store, two views because my products could come from either my packages table or sessions table. The prices are joined from the price table.
The two views work fine on their own, but when I combine there usage I get no results.
I want my shipping guy to only see the items he should ship, not the digital items, namely, those with delivery format equal to 'DVD'.
session view:
select
`ats_store`.`order`.`payment_type` AS `payment_type`,
`production_www`.`paypal_transactions`.`createdOn` AS `order_date`,
`production_www`.`paypal_transactions`.`FIRSTNAME` AS `firstname`,
`production_www`.`paypal_transactions`.`LASTNAME` AS `lastname`,
`ats_store`.`order`.`id` AS `order_id`,
`ats_store`.`order`.`customerid` AS `customerid`,
`ats_store`.`order`.`shipping_address_id` AS `shipping_address_id`,
`ats_store`.`order`.`status` AS `status`,
`ats_store`.`order_items`.`product_id` AS `product_id`,
`ats_store`.`shipping_address`.`country` AS `country`,
`ats_store`.`shipping_address`.`street_1` AS `street1`,
`ats_store`.`shipping_address`.`street_2` AS `street2`,
`ats_store`.`shipping_address`.`street_3` AS `street3`,
`ats_store`.`shipping_address`.`city` AS `city`,
`ats_store`.`shipping_address`.`state_and_province` AS `state`,
`ats_store`.`shipping_address`.`zip_code` AS `zip`,
group_concat(`production_cache_salesforce_repl`.`ats_conference_session__c`.`Name`,
'(',
`price`.`Delivery_Format__c`,
')'
order by `production_cache_salesforce_repl`.`ats_conference_session__c`.`sort_code__c` ASC
separator ',') AS `session_products`
from
(((((`ats_store`.`order`
left join `ats_store`.`shipping_address` ON ((`ats_store`.`shipping_address`.`id` = `ats_store`.`order`.`shipping_address_id`)))
join `ats_store`.`order_items` ON ((`ats_store`.`order_items`.`order_id` = `ats_store`.`order`.`id`)))
join `production_cache_salesforce_repl`.`ats_store_price__c` `price` ON ((`price`.`Id` = `ats_store`.`order_items`.`product_id`)))
join `production_cache_salesforce_repl`.`ats_conference_session__c` ON ((`production_cache_salesforce_repl`.`ats_conference_session__c`.`Id` = convert( `price`.`ATS_Conference_Session__c` using utf8))))
left join `production_www`.`paypal_transactions` ON ((`production_www`.`paypal_transactions`.`id` = `ats_store`.`order`.`paypal_transaction_id`)))
where
((`ats_store`.`order`.`shipping_address_id` <> -(1))
and (`production_www`.`paypal_transactions`.`COMMENT1` = 'ATS Web Store')
and (`production_www`.`paypal_transactions`.`RESPMSG` = 'Approved')
and (`price`.`Delivery_Format__c` = 'DVD'))
group by `ats_store`.`order`.`id`
order by `production_www`.`paypal_transactions`.`createdOn`
package view:
select
`ats_store`.`order`.`payment_type` AS `payment_type`,
`production_www`.`paypal_transactions`.`createdOn` AS `order_date`,
`production_www`.`paypal_transactions`.`FIRSTNAME` AS `firstname`,
`production_www`.`paypal_transactions`.`LASTNAME` AS `lastname`,
`ats_store`.`order`.`id` AS `order_id`,
`ats_store`.`order`.`customerid` AS `customerid`,
`ats_store`.`order`.`shipping_address_id` AS `shipping_address_id`,
`ats_store`.`order`.`status` AS `status`,
`ats_store`.`order_items`.`product_id` AS `product_id`,
`ats_store`.`shipping_address`.`country` AS `country`,
`ats_store`.`shipping_address`.`street_1` AS `street1`,
`ats_store`.`shipping_address`.`street_2` AS `street2`,
`ats_store`.`shipping_address`.`street_3` AS `street3`,
`ats_store`.`shipping_address`.`city` AS `city`,
`ats_store`.`shipping_address`.`state_and_province` AS `state`,
`ats_store`.`shipping_address`.`zip_code` AS `zip`,
`price`.`Delivery_Format__c` AS `delivery_format`,
group_concat(`production_cache_salesforce_repl`.`ats_store_package__c`.`Package_Code__c`,
'(',
`price`.`Delivery_Format__c`,
')'
order by `production_cache_salesforce_repl`.`ats_store_package__c`.`Package_Code__c` ASC
separator ',') AS `package_products`
from
(((((`ats_store`.`order`
left join `ats_store`.`shipping_address` ON ((`ats_store`.`shipping_address`.`id` = `ats_store`.`order`.`shipping_address_id`)))
join `ats_store`.`order_items` ON ((`ats_store`.`order_items`.`order_id` = `ats_store`.`order`.`id`)))
join `production_cache_salesforce_repl`.`ats_store_price__c` `price` ON ((`price`.`Id` = `ats_store`.`order_items`.`product_id`)))
join `production_cache_salesforce_repl`.`ats_store_package__c` ON ((`production_cache_salesforce_repl`.`ats_store_package__c`.`Id` = convert( `price`.`ATS_Store_Package__c` using utf8))))
left join `production_www`.`paypal_transactions` ON ((`production_www`.`paypal_transactions`.`id` = `ats_store`.`order`.`paypal_transaction_id`)))
where
((`ats_store`.`order`.`shipping_address_id` <> -(1))
and (`production_www`.`paypal_transactions`.`COMMENT1` = 'ATS Web Store')
and (`production_www`.`paypal_transactions`.`RESPMSG` = 'Approved')
and (`price`.`Delivery_Format__c` = 'DVD'))
group by `ats_store`.`order`.`id`
order by `production_www`.`paypal_transactions`.`createdOn`
Here is the query that uses both views:
select *,group_concat(pv.package_products,',',sv.session_products) as products from ats_store.shipping_orders_sessions_view sv,ats_store.shipping_orders_packages_view pv
where sv.order_id = pv.order_id
group by sv.order_id
If I remove from the VIEWS and (price.Delivery_Format__c= 'DVD') I can get the orders, but the digital and DVD products are shown. I only want my shipping guy to see items he should ship, the DVDs.

Assuming your two views are clean and working:
Let's work on that query that joins them.
Here's what you have, rewritten to use an explicit join.
select *, /* query with problems */
group_concat(pv.package_products,',',sv.session_products) as products
from ats_store.shipping_orders_sessions_view sv
join ats_store.shipping_orders_packages_view pv on sv.order_id = pv.order_id
group by sv.order_id
What's wrong here?
First, pro tip: don't use select * in complex queries.
Second, select * guarantees random nonsense in your output resultset because you're using a harmful nonstandard MySQL hackstension. Read this: http://dev.mysql.com/doc/refman/5.5/en/group-by-extensions.html You should mention each non-aggregated column in your result set in your GROUP BY clause.
Third, you probably want
GROUP_CONCAT(CONCAT(pv.package_products,',',sv.session_product)) AS products
to make sure you are using the aggregating function GROUP_CONCAT() properly.
So here is a better query.
select sv.order_id,
GROUP_CONCAT(CONCAT(pv.package_products,',',sv.session_product)) AS products
from ats_store.shipping_orders_sessions_view sv
join ats_store.shipping_orders_packages_view pv on sv.order_id = pv.order_id
group by sv.order_id
EDIT: This will yield a result set with one row per distinct order_id that mentions all the products in each order. If you wish to include only certain items, for example those with delivery_format = 'DVD', you need to add a WHERE clause. It looks like your package view exposes that column. So try this:
select sv.order_id,
GROUP_CONCAT(CONCAT(pv.package_products,',',sv.session_product)) AS products
from ats_store.shipping_orders_sessions_view sv
join ats_store.shipping_orders_packages_view pv on sv.order_id = pv.order_id
where pv.delivery_format = 'DVD'
group by sv.order_id
I don't understand your schema well enough to give you better advice than this. I can see that your views are quite complex.

Related

Get 50k rows faster with subqueries - Laravel 5.6

The below query is to get the candidate's details from the table which has 50k rows. Including jobs, regions, and employment types. A candidate has basic details with employment type, jobs, regions are in another table with foreign key relation.
$candidates = DB::table('candidates')
->join('role_users', function($join){
$join->on('candidates.user_id', '=', 'role_users.user_id');
$join->where('role_users.role_id', 6);
})
->join('candidate_statuses', 'candidates.candidate_status_id', '=', 'candidate_statuses.id')
->join('employment_types', 'candidates.employment_types_id', '=', 'employment_types.id')
->select(
'candidates.id',
'candidates.user_id',
'candidates.candidate_code',
'candidates.full_name as name',
'employment_types.title AS employment_type',
DB::raw("(SELECT GROUP_CONCAT(candidate_jobs.job_id SEPARATOR ',') FROM candidate_jobs WHERE candidate_jobs.candidate_id = candidates.id) as job_ids"),
DB::raw("(SELECT GROUP_CONCAT(regions.name SEPARATOR ',') FROM candidate_regions INNER JOIN regions ON regions.id=candidate_regions.region_id WHERE candidate_regions.candidate_id = candidates.id) as regions"),
'role_users.email',
'role_users.login_at',
'candidates.is_deleted')
->where('candidates.candidate_status_id', '!=' , 6)
->where('candidates.is_deleted', $request->is_deleted)
->orderBy('candidates.first_name')
->groupBy('candidates.id')
->paginate(10);
}
select `candidates`.`id`, `candidates`.`user_id`, `candidates`.`candidate_code`,
`candidates`.`first_name`,
`candidates`.`last_name`, `candidates`.`full_name` as `name`,
`candidates`.`profile_img`, `candidates`.`candidate_status_id`,
`candidates`.`employment_types_id`,
`employment_types`.`title` as `employment_type`,
(
SELECT GROUP_CONCAT(candidate_jobs.job_id SEPARATOR ',')
FROM candidate_jobs
WHERE candidate_jobs.candidate_id = candidates.id
) as job_ids,
(
SELECT GROUP_CONCAT(regions.name SEPARATOR ',')
FROM candidate_regions
INNER JOIN regions ON regions.id=candidate_regions.region_id
WHERE candidate_regions.candidate_id = candidates.id
) as regions,
`candidates`.`formatted_mobile_number`, `candidates`.`place`,
`candidates`.`post_code`, `role_users`.`email`, `role_users`.`login_at`,
`role_users`.`email`, `candidates`.`has_access`, `candidates`.`is_deleted`
from `candidates`
inner join `role_users` ON `candidates`.`user_id` = `role_users`.`user_id`
and `role_users`.`role_id` = ?
inner join `candidate_statuses`
ON `candidates`.`candidate_status_id` = `candidate_statuses`.`id`
inner join `employment_types`
ON `candidates`.`employment_types_id` = `employment_types`.`id`
where (`candidates`.`candidate_status_id` in (?))
and `candidates`.`candidate_status_id` != ?
and `candidates`.`is_deleted` = ?
group by `candidates`.`id`
order by `candidates`.`first_name` asc
It takes 2/ 3 seconds to get the result in my local machine but in production takes too long time.
Can anyone please help?
It seems like the second part is unnecessary:
`candidates`.`candidate_status_id` in (?))
and `candidates`.`candidate_status_id` != ?
Making these match avoids an extra pass over the results
group by `first_name`, `id`
order by `first_name` asc, id
Possibly helpful indexes:
candidates: INDEX(candidate_status_id, is_deleted, first_name, id, user_id)
role_users: INDEX(user_id, email, login_at, role_id)
candidate_jobs: INDEX(candidate_id, job_id)
candidate_regions: INDEX(candidate_id, region_id)

Sql query to search for multiple match in junction table

Hello, I have two tables and one junction table. And I want to find all estates, that has one or multiple comforts. I wrote this query (postrgreSql):
SELECT DISTINCT "estates".*
FROM "estates"
LEFT JOIN "estate_comforts"
ON "estates"."id" = "estate_comforts"."estate_id"
WHERE "estate_comforts"."comfort_id" IN ( '1', '2' )
It finding estates that has the first comfort OR the second, but I need to search in "AND" mode.
This project using Yii2 framewors, so plain sql or ActiveRecord statement are acceptable.
Update. This query select all esates, regardless of the comforts
SELECT DISTINCT "estates".*
FROM "estates"
LEFT JOIN "estate_comforts"
ON "estates"."id" = "estate_comforts"."estate_id"
AND "estate_comforts"."comfort_id" IN ( '1', '2' )
Either JOIN estate_comforts twice, one time for comfort_id 1, and another time for comfort_id 2:
SELECT DISTINCT "estates".*
FROM "estates"
INNER JOIN "estate_comforts" ec1
ON "estates"."id" = ec1."estate_id"
INNER JOIN "estate_comforts" ec2
ON "estates"."id" = ec2."estate_id"
WHERE ec1."comfort_id" = '1'
AND ec2."comfort_id" = '2'
Alternatively, do a GROUP BY on estate_comforts to find estate_id with at least two different comfort_id values. Join with that result:
select e.*
from "estates" e
join (select "estate_id"
from "estate_comforts"
WHERE "comfort_id" IN ( '1', '2' )
group by "estate_id"
having count(distinct "comfort_id") >= 2) ec ON e."id" = ec."estate_id"

What is wrong with my MySQL query (UNION)?

I hope you can get me here. I'm working on a PMS system and I want to run a simple SELECT statement to get a list of secret codes for my customers based on which of their parcels have arrived and the transaction type. The 'transferConfirmation' table already has the customer ID and the secret code and displays one row for each instance but for some reason, when i use the following query:
SELECT `tc`.`transferID` AS `transferID`,
`c`.`firstName` AS `firstName`,
`c`.`lastName` AS `lastName`,
`tc`.`secretCode` AS `secretCode`,
'Purchase Order' AS `transactionType`
FROM (((((`db`.`transferconfirmation` `tc`
JOIN `db`.`customer` `c` on((`tc`.`customerID` = `c`.`customerID`)))
JOIN `db`.`transfer` `t` on((`t`.`transferID` = `tc`.`transferID`)))
JOIN `db`.`transferentry` `te` on((`t`.`transferID` = `te`.`transferID`)))
JOIN `db`.`purchaseorder` `po` on((`te`.`referenceID` = `po`.`purchaseOrderID`)))
JOIN `db`.`purchaseorderentry` `poe` on(((`po`.`purchaseOrderID` = `poe`.`purchaseOrderID`)
AND (`te`.`referenceEntryID` = `poe`.`purchaseOrderEntryID`))))
WHERE (`poe`.`collectionStatus` = 0)
UNION ALL
SELECT `tc`.`transferID` AS `transferID`,
`c`.`firstName` AS `firstName`,
`c`.`lastName` AS `lastName`,
`tc`.`secretCode` AS `secretCode`,
'Shipping Order' AS `transactionType`
FROM (((((`db`.`transferconfirmation` `tc`
JOIN `db`.`customer` `c` on((`tc`.`customerID` = `c`.`customerID`)))
JOIN `db`.`transfer` `t` on((`t`.`transferID` = `tc`.`transferID`)))
JOIN `db`.`transferentry` `te` on((`t`.`transferID` = `te`.`transferID`)))
JOIN `db`.`shippingorder` `so` on((`te`.`referenceID` = `so`.`shippingOrderID`)))
JOIN `db`.`shippingorderentry` `soe` on(((`so`.`shippingOrderID` = `soe`.`shippingOrderID`)
AND (`te`.`referenceEntryID` = `soe`.`shippingOrderEntryID`))))
WHERE (`soe`.`collectionstatus` = 0)
I keep getting results with duplicate data only difference is with the transaction. I guess my question is, if the secret code has been selected once, how to I prevent any other row from displaying the same code.
Use the UNION operator. The UNION operator will remove duplicate records from each query (UNION ALL would retain them).
SELECT transferID, firstName, lastName, secretCode,
'Purchase Order' AS transactionType
FROM ...
UNION
SELECT transferID, firstName, lastName, secretCode,
'Purchase Order' AS transactionType
FROM ...

MySQL Database design advice - using joins

I am building an AJAX like search page which allows a customer to select a number filters that will narrow down the search. For instance, a user has selected an 'iPhone 5' and has additional filters for capacity (32GB, 64GB) & colour (black, white..).
The user can only select a single radio box per category (so they could select 32GB & Black).. but they could not select (32GB & 64GB & black as two of these belong to the 'capacity' category).
I have added the schema here on sqlfiddle (please ignore the fact i've removed the primary keys they exist in the proper app they have just been removed along with some other fields/data to minimise the sqlfiddle)
http://sqlfiddle.com/#!2/964425
Can anyone suggest the best way to create the query to do the following:
Get all the prices for device_id '2939' (iPhone 5) which has the 'attributes' of '32GB' AND 'Black'
I currently have this - but this only works when selecting for a single attribute:
// search for device with '64GB' & 'Black' attributes (this currently doesn't return any rows)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19'
AND `attribute_option_id` = '47';
// search for device with '64GB' attribute only (this currently DOES return a row)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19';
Any advice on the database design would be appreciated too
Note: I was thinking to have a new column within the 'prices' table that has the matching attribute_ids serialised - would this be not good for optimisation however (e.g would it be slower than the current method)
Since attribute_option_id is an atomic value, it cannot have two different values for the same row. So your WHERE clause cannot match any record:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19' # Here for one row, attribute_option_id is either 19
AND `attribute_option_id` = '47'; # of '47'. Cannot be the both
Instead of JOIN, you could try a subquery if you feel that is more readable. I think MySQL allow that syntax:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`device_id` = '2939'
AND EXISTS (SELECT *
FROM prices_attributes
WHERE price_id = `prices`.`id`
AND attribute_option_id IN ('19', '47') )
I don't know how MySQL will optimize the above solution. An alternative would be:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`id` IN (
SELECT DISTINCT `price_id`
FROM prices_attributes
WHERE attribute_option_id IN ('19', '47')
)
I think you should use the IN operator for the attribute_option_id and you set the values dynamically to the query; Also, using group_by you have only one row per price so in effect you get all the prices. Apart from this, the design is ok.
Here, I have made an example:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
and `attribute_option_id` in ('19','47')
group by `prices`.`device_id`, `prices`.`price`;
Here, you can also add an order clause to order by price:
order by `prices`.`price` desc;
Another way to solve this would be to use a distinct on price, like this:
select distinct(prices.price)
from prices
where prices.device_id = 2939
and id in (select price_id from prices_attributes where attribute_option_id in (19,47));
Join against the devices_attributes_options table several times, once for each attribute the item must have
Something like this:-
SELECT *
FROM devices a
INNER JOIN prices b ON a.id = b.device_id
INNER JOIN prices_attributes c ON b.id = c.price_id
INNER JOIN devices_attributes_options d ON c.attribute_option_id = d.id AND d.attribute_value = '32GB'
INNER JOIN devices_attributes_options e ON c.attribute_option_id = e.id AND e.attribute_value = 'Black'
WHERE a.id = 2939
As to putting serialised details into a field, this is a really bad idea and would come back to bite you in the future!
SELECT * FROM prices WHERE device_id=2939 AND id IN (SELECT price_id FROM prices_attributes WHERE attribute_option_id IN (19,47));
Is it what you're looking for?
EDIT: sorry, didn't notice you're asking for query using joins

Mysql query Timeout issue

I have an application where a lot of options can be selected where there are four tables and possible 4 different terms from each filter and also additional for if we take value count and i am no good with queries just know how to get the desired result.So the queries can be like:
select distinct(maindbgroupid) from ((SELECT mainDBGroupID from
groupstatisticsdata.seniority where ( `name` = 'Senior' )AND maindbgroupid in (SELECT
mainDBGroupID from groupstatisticsdata.Function where ( `name` = 'Information Technology'
)AND (`Value` >1000) AND maindbgroupid in (SELECT mainDBGroupID from
groupstatisticsdata.Industry where ( `name` = 'Information Technology and Services' )AND
(`Value` >1000))))) t join maingroupdb.campaigns on campaigns.groupid=t.maindbgroupid
where campaigns.campaignName='Campaign1101'
This is one example , how to improve it please as i always get timeout when i use more than one filter.
What i am doing in the query is an intersect between the tables.
Try this ::
select distinct(mainDBGroupID)
from
groupstatisticsdata.seniority
inner join groupstatisticsdata.Function on (groupstatisticsdata.seniority.maindbgroupid=groupstatisticsdata.Function.mainDBGroupID)
inner join groupstatisticsdata.Industry on (groupstatisticsdata.Industry.mainDBGroupID=groupstatisticsdata.seniority.maindbgroupid)
inner join maingroupdb.campaigns on campaigns.groupid=t.maindbgroupid
where groupstatisticsdata.seniority.`name` = 'Senior' AND
groupstatisticsdata.Function.`name` = 'Information Technology'
AND `Value` >1000
AND maindbgroupid
AND groupstatisticsdata.Industry.`name` = 'Information Technology and Services' )
AND
groupstatisticsdata.Industry.`Value` >1000
AND campaigns.campaignName='Campaign1101'