SELECT CASE WHEN THEN (SELECT) - mysql

I am trying to select a different set of results for a product depending on a product type.
So if my product should be a book I want it to look up the UPC and Artist for a normal product these details are however irrelevant and for another product I would want a completely different set of results.
SELECT CASE Product.type_id
WHEN 10 THEN (
SELECT
Product.product_id,
Product.type_id,
Product.product_name,
Product.UPC,
Product_Type.type,
CONCAT_WS(' ' , first_name, middle_name, last_name ) AS artistC
FROM Product, Product_Type, Product_ArtistAuthor
WHERE Product.type_id = Product_Type.type_id
AND Product.product_id = $pid
AND Product.artist_id = Product_ArtistAuthor.artist_id
)
ELSE (
SELECT
Product.product_id,
Product.type_id,
Product.product_name,
Product_Type.type
FROM Product, Product_Type
WHERE Product.type_id = Product_Type.type_id
AND Product.product_id = $pid
)
END
FROM Product
WHERE Product.product_id = $pid
I am not sure where I am going wrong

You Could try the other format for the case statement
CASE WHEN Product.type_id = 10
THEN
(
Select Statement
)
ELSE
(
Other select statement
)
END
FROM Product
WHERE Product.product_id = $pid
See http://msdn.microsoft.com/en-us/library/ms181765.aspx for more information.

You should avoid using nested selects and I would go as far to say you should never use them in the actual select part of your statement. You will be running that select for each row that is returned. This is a really expensive operation. Rather use joins. It is much more readable and the performance is much better.
In your case the query below should help. Note the cases statement is still there, but now it is a simple compare operation.
select
p.product_id,
p.type_id,
p.product_name,
p.type,
case p.type_id when 10 then (CONCAT_WS(' ' , first_name, middle_name, last_name )) else (null) end artistC
from
Product p
inner join Product_Type pt on
pt.type_id = p.type_id
left join Product_ArtistAuthor paa on
paa.artist_id = p.artist_id
where
p.product_id = $pid
I used a left join since I don't know the business logic.

For a start the first select has 6 columns and the second has 4 columns. Perhaps make both have the same number of columns (adding nulls?).

I ended up leaving the common properties from the SELECT queries and making a second SELECT query later on in the page. I used a php IF command to call for different scripts depending on the first SELECT query, the scripts contained the second SELECT query.

Related

Fetching data from many to many relationship

I am having 3 tables namely
category
case
cost
office_id
category_id
linked to >
category_id
case_number
linked to >
case_number
total_cost
Problem:
I need to fetch the total number of case and their respective cost for every office id which is there in category table
Query I have written:
select cm_c_d.case_number,cm_c.office_id,count(*) as case_count from categories as cm_c
join case as cm_c_d on cm_c.category_id = cm_c_d.category_id
join cost on cm_c_d.case_number = cost.case_number group by office_id;
but I don't think this will provide the desired result as joining all the three tables will increase the row count.
Updated SQL query:
select cm_c.office_id
, count(DISTINCT cm_costs.case_number) as case_count
, SUM(total_charge) AS overall_cost
from cm_categories as cm_c
JOIN cm_case_details as cm_c_d
on cm_c.category_id = cm_c_d.category_id
join cm_costs
on cm_c_d.case_number = cm_costs.case_number
group by cm_c.office_id
;
If you want distinct case count (adjusted with the new table names):
SELECT cm_c.office_id
, COUNT(DISTINCT cm_costs.case_number) AS case_count
, SUM(total_charge) AS overall_cost
FROM cm_categories AS cm_c
JOIN cm_case_details AS cm_c_d ON cm_c.category_id = cm_c_d.category_id
JOIN cm_costs ON cm_c_d.case_number = cm_costs.case_number
GROUP BY cm_c.office_id
;
COUNT(DISTINCT x) means count the distinct number of x values for each group.
Also notice we needed to quote the case table name. As mentioned in prior comments, that's a reserved word. I suggest avoiding use of reserved words as identifiers (table, column, etc names).
I've removed case_number from your SELECT list because it's not functionally dependent on the GROUP BY terms. That means the design/query does not guarantee that there is at most one case_number for each office_id.
If you want a case_number in the select list, you would need to use a form of aggregation (like COUNT), as follows:
SELECT cm_c.office_id
, COUNT(DISTINCT cm_costs.case_number) AS case_count
, SUM(total_charge) AS overall_cost
, MIN(cost.case_number) AS some_case_number
, GROUP_CONCAT(cost.case_number) AS all_cases
FROM cm_categories AS cm_c
JOIN cm_case_details AS cm_c_d ON cm_c.category_id = cm_c_d.category_id
JOIN cm_costs ON cm_c_d.case_number = cm_costs.case_number
GROUP BY cm_c.office_id
;
Be careful of GROUP_CONCAT for very large sets, since that result column width could get rather wide.
Here's the test case, with necessary adjustments:
Test case
For more detail on GROUP BY and functional dependence, see the following links:
group-by-handling
group-by-functional-dependence

Performance: Double nested subquery unknown column

If I do COUNT()/AVG() in the first subquery MySQL process all rows inside the table, because of that reason it is necessary to filter at from all rows with another subquery.
As example if I have 3 rows, but only 1 row has the id which should get count, MySQL process all 3 rows (according to EXPLAIN) and does the where clause after.
If I'm able to select in a double nested sub query this single row and call the count outside it would be a lot better performance wise.
The problem MySQL does not allow using outer values in a second level subquery.
Simple example of my code:
SELECT
pr.id, pr.catid, ...
(
SELECT COUNT(pra.id)
FROM (
SELECT id
FROM productsrating
WHERE pr.id = productid
) pra
) as ratingcount,
...
FROM
(
SELECT id, ...
FROM products
WHERE active = 1
) pr
-> Unknown column pr.id
I do also tried to use the COUNT in the main select but it isn't allowed to have multiple values inside a subquery.
Edit: I have an index on productid.
EDIT2 SOLUTION:
Sorry at all its working fine with the first single subquery, server problems caused bad behavior.
It seems you want the count of ratings occurring for an active product. Is this correct?
So; why is a simple left join not working? The count of PRA should be based on only those products which are active; so index usage should work here.
I'd need to see sample data / expected results to figure out the overall goal here.
SELECT PR.*, count(PRA.ID)
FROM products PR
LEFT JOIN productsRating PRA
on PR.ID = PRA.ProductID
WHERE PR.Active = 1
GROUP BY PR.*
Substitute all fields needed for PR.*
Maybe this... seems like an odd thing to have to do to get the products rating to be filtered before the average/count is done though.
SELECT PR.*, count(PRA.ID)
FROM products PR
LEFT JOIN (SELECT * FROM productsRating PRI
WHERE EXISTS (SELECT 1
FROM Products P
WHERE active = 1 and PRI.ProductID = P.ID)) PRA
on PR.ID = PRA.ProductID
WHERE PR.Active = 1
GROUP BY PR.*
Try count with distinct
SELECT
pr.id, pr.catid, ...
(
SELECT COUNT(Distinct productsrating.id)
FROM productsrating
WHERE pr.id = productid
) as ratingcount,
...
FROM
(
SELECT id, ...
FROM products
WHERE active = 1
) pr

NOT LIKE on mysql query

I have a big problem understanding why a query works when using LIKE in query and not working when using NOT LIKE.
I will post query below:
select DISTINCT (mails), name
from disposable
JOIN (
SELECT DISTINCT (mail) as mails,
CONCAT(toys.firstname, ' ' , toys.lastname) as name
FROM toys2
join toys ON toys.userid = toys2.id
where ( (toyid = '27' or toyid = '29')
and status != 'Sold'
and toys.regdate >= '2017-01-01'
)
) as tab
WHERE tab.mails LIKE CONCAT('%#', disposable.email)
I think what you want is something more like the following. Note that I simplified the schema a bit so as to do a bit less work for the SQL Fiddle.
SELECT c.email, c.name
FROM customer c LEFT JOIN disposable d
ON SUBSTR(c.email, INSTR(c.email, '#')+1, LENGTH(c.email)-INSTR(c.email, '#')) = d.email
WHERE d.email IS NULL;
Basically, here you're getting the domain of the customer and matching it to the entry in the disposable table. The final WHERE clause uses IS NULL to determine the customer email addresses that are not disposable - use IS NOT NULL to find the ones that are.
Hope this helps.

Combining 2 SELECT query using LEFT OUTER JOIN

How do i combined this to two select query using left join syntax. (My query has error and I can't find a solution)
select
*
from
(select
mi.parent_entity_id entity,
tctp.institution_rec_id institutionRecId,
institution_code storecode,
institution_name storename,
case when sum(unsettled_points) is null
then coalesce (sum(point_value),0)
else coalesce
(sum(unsettled_points),0) end sumpoints
from
t_card_transaction_point tctp
inner join
m_institution mi on tctp.institution_rec_id=mi.institution_rec_id
where
mi.parent_entity_id = 70125 and
tctp.point_status = 'xy4604'
group by
entity,
institutionRecId,
storecode,
storename
) storeExpired
left join
entityExpired on storeExpired.entity=entityExpired.entity
(select
mpb.institution_rec_id entity,
tctd.institution_rec_id institutionRecId,
tctd.card_no cardnumber,
total_amount_primary totalpoints,
case when total_unsettled_points is null
then point_value
else tctd.total_unsettled_points end
points
from
t_card_transaction_detail tctd inner
join
m_point_bucket mpb on mpb.card_no=tctd.card_no
inner join
m_institution mi on mi.institution_rec_id=tctd.institution_rec_id
where
mpb.total_amount_primary > 1000 and
tctd.adjustment_date is null
group by
entity,
institutionRecId,
cardnumber,
totalpoints,
points
) entityExpired
Firstly:
We do appreciate proper indenting / lining of code for ease of readability :)
Second:
"My query has error" is not particularly explanatory.
Anywho, to answer your question:
SQL has an order of operation of
From
Where
Group By
Having
Select
Order By
This means that the alias are created when the select is executed. And since "group by" is executed before this, the alias's doesn't exist yet - this is probably the error you are getting.
Also, I'm not sure if MySQL does allow a join on a alias, which is defined further down in the query (I could be wrong though), so i would move the query itself into the join brackets, and use on the "on"-clause afterwards.
Sample query: (Not tested, since I doesn't have the tables)
select
*
from (
select
mi.parent_entity_id as entity
, tctp.institution_rec_id as institutionRecId
, institution_code as storecode
, institution_name as storename
, case when sum(unsettled_points) is null
then coalesce (sum(point_value),0)
else coalesce (sum(unsettled_points),0)
end as sumpoints
from t_card_transaction_point tctp
inner join m_institution mi on tctp.institution_rec_id = mi.institution_rec_id
where 1=1
and mi.parent_entity_id = 70125
and tctp.point_status = 'xy4604'
group by
mi.parent_entity_id
, tctp.institution_rec_id
, institution_code
, institution_name
) storeExpired
left join (
select
mpb.institution_rec_id as entity
, tctd.institution_rec_id as institutionRecId
, tctd.card_no as cardnumber
, total_amount_primary as totalpoints
, case when total_unsettled_points is null
then point_value
else tctd.total_unsettled_points
end as points
from t_card_transaction_detail tctd
inner join m_point_bucket mpb on mpb.card_no=tctd.card_no
inner join m_institution mi on mi.institution_rec_id=tctd.institution_rec_id
where 1=1
and mpb.total_amount_primary > 1000
and tctd.adjustment_date is null
group by
mpb.institution_rec_id
, tctd.institution_rec_id
, tctd.card_no
, total_amount_primary
, case when total_unsettled_points is null
then point_value
else tctd.total_unsettled_points
end
) entityExpired on storeExpired.entity=entityExpired.entity
Edit:
I just google'd it, and you can in fact use alias's in your group by statement in MySQL (Not allowed in MSSQL, nor is it ANSI standard).
However, after seeing your comment regarding the error, it is probably due to the fact that you are joining with the alias entityExpired, before the subquery is created. I'm guessing that moving the subquery, as I've done in the example, should work.

optimize Mysql: get latest status of the sale

In the following query, I show the latest status of the sale (by stage, in this case the number 3). The query is based on a subquery in the status history of the sale:
SELECT v.id_sale,
IFNULL((
SELECT (CASE WHEN IFNULL( vec.description, '' ) = ''
THEN ve.name
ELSE vec.description
END)
FROM t_record veh
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign
INNER JOIN t_state ve ON ve.id_state = vec.id_state
WHERE veh.id_sale = v.id_sale
AND vec.id_stage = 3
ORDER BY veh.id_record DESC
LIMIT 1
), 'x') sale_state_3
FROM t_sale v
INNER JOIN t_quarters sd ON v.id_quarters = sd.id_quarters
WHERE 1 =1
AND v.flag =1
AND v.id_quarters =4
AND EXISTS (
SELECT '1'
FROM t_record
WHERE id_sale = v.id_sale
LIMIT 1
)
the query delay 0.0057seg and show 1011 records.
Because I have to filter the sales by the name of the state as it would have to repeat the subquery in a where clause, I have decided to change the same query using joins. In this case, I'm using the MAX function to obtain the latest status:
SELECT
v.id_sale,
IFNULL(veh3.State3,'x') AS sale_state_3
FROM t_sale v
INNER JOIN t_quarters sd ON v.id_quarters = sd.id_quarters
LEFT JOIN (
SELECT veh.id_sale,
(CASE WHEN IFNULL(vec.description,'') = ''
THEN ve.name
ELSE vec.description END) AS State3
FROM t_record veh
INNER JOIN (
SELECT id_sale, MAX(id_record) AS max_rating
FROM(
SELECT veh.id_sale, id_record
FROM t_record veh
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign AND vec.id_stage = 3
) m
GROUP BY id_sale
) x ON x.max_rating = veh.id_record
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign
INNER JOIN t_state ve ON ve.id_state = vec.id_state
) veh3 ON veh3.id_sale = v.id_sale
WHERE v.flag = 1
AND v.id_quarters = 4
This query shows the same results (1011). But the problem is it takes 0.0753 sec
Reviewing the possibilities I have found the factor that makes the difference in the speed of the query:
AND EXISTS (
SELECT '1'
FROM t_record
WHERE id_sale = v.id_sale
LIMIT 1
)
If I remove this clause, both queries the same time delay... Why it works better? Is there any way to use this clause in the joins? I hope your help.
EDIT
I will show the results of EXPLAIN for each query respectively:
q1:
q2:
Interesting, so that little statement basically determines if there is a match between t_record.id_sale and t_sale.id_sale.
Why is this making your query run faster? Because Where statements applied prior to subSelects in the select statement, so if there is no record to go with the sale, then it doesn't bother processing the subSelect. Which is netting you some time. So that's why it works better.
Is it going to work in your join syntax? I don't really know without having your tables to test against but you can always just apply it to the end and find out. Add the keyword EXPLAIN to the beginning of your query and you will get a plan of execution which will help you optimize things. Probably the best way to get better results in your join syntax is to add some indexes to your tables.
But I ask you, is this even necessary? You have a query returning in <8 hundredths of a second. Unless this query is getting ran thousands of times an hour, this is not really taxing your DB at all and your time is probably better spent making improvements elsewhere in your application.