MySQL: Where this is true, if not, then this - mysql

My query:
SELECT *
FROM ranks
WHERE
(price = 25.00 AND accumulate = 0)
OR
(price <= (SELECT SUM(amount) FROM donations WHERE username = 'username' AND amount IN (SELECT price FROM ranks WHERE accumulate = 1)))
ORDER BY price
DESC LIMIT 1
Basically I want to return the first where clause if it finds a match, otherwise return the second clause.

This query will work
SELECT *
FROM ranks
WHERE price = 25.00 AND accumulate = 0
UNION ALL
SELECT *
FROM ranks
WHERE NOT EXISTS(SELECT *
FROM ranks
WHERE price = 25.00 AND accumulate = 0)
AND (price <= (SELECT SUM(amount) FROM donations WHERE username = 'username' AND amount IN (SELECT price FROM ranks WHERE accumulate = 1)))
ORDER BY price
DESC LIMIT 1

Try this query with IF
SELECT *
FROM ranks
WHERE if(price = 25.00,accumulate = 0, price <= (SELECT
SUM(amount)
FROM donations
WHERE username = 'username'
AND amount IN(SELECT
price
FROM ranks
WHERE accumulate = 1)))
ORDER BY price DESC
LIMIT 1

Related

Is 1.3s Slow for a Query with GPS, Text, & Ranked Keywords?

I've got a MySQL database with about 80K products, 300 store locations, and pricing info. Some products have prices, some don't. I'm running search on the product names (text), tags associated with them (text), ranks (integers), omit_tags that searches & products that don't go together, and geolocation based on a users location and distance they specify from the stores location. My search times are averaging 1.3s to return 10 results. Would this be considered slow?
Here's what my query looks like.
SELECT DISTINCT t3.prod_id,
t3.awaiting_approval,
t3.brand,
t3.prod_name,
t3.size,
t3.units,
t3.category,
t3.image,
t3.url,
t3.quantity,
t3.rank,
t3.word_count,
t3.word_count
FROM ((SELECT item_info_mem.prod_id,
item_info_mem.awaiting_approval,
item_info_mem.prod_name,
item_info_mem.brand,
item_info_mem.size,
item_info_mem.units,
item_info_mem.category,
(SELECT rank
FROM search_tags_mem
WHERE prod_id = item_info_mem.prod_id
AND tag = "bread"
ORDER BY rank
LIMIT 1) AS
rank,
(SELECT image
FROM images_mem
WHERE prod_id = item_info_mem.prod_id
ORDER BY id
LIMIT 1) AS
image,
(SELECT url
FROM urls_mem
WHERE prod_id = item_info_mem.prod_id
AND disabled IS NULL
ORDER BY id DESC
LIMIT 1) AS
url,
(SELECT quantity
FROM shopping_list_mem
WHERE user_id = "1"
AND prod_id = item_info_mem.prod_id
ORDER BY id
LIMIT 1) AS
quantity,
( Substrcount(Lcase(item_info_mem.prod_name), Lcase("bread"))
+ Substrcount(Lcase(item_info_mem.brand), Lcase("bread")) ) AS
word_count,
( Substrcheck(Lcase(item_info_mem.prod_name),
Lcase(item_info_mem.brand), Lcase("bread")) ) AS
word_count_unique
FROM item_info_mem
WHERE NOT EXISTS (SELECT id
FROM search_tags_omit_mem
WHERE prod_id = item_info_mem.prod_id
AND tag = "bread"
ORDER BY id DESC
LIMIT 1)
AND ( item_info_mem.prod_name REGEXP "bread"
OR item_info_mem.brand REGEXP "bread" )
AND EXISTS(SELECT scans_mem.scan_id
FROM scans_mem,
stores_mem
WHERE scans_mem.price IS NOT NULL
AND scans_mem.expired IS NULL
AND item_info_mem.prod_id = scans_mem.prod_id
AND NOT
EXISTS (SELECT
user_stores_disabled_mem.user_id
FROM
user_stores_disabled_mem
,
stores_mem
WHERE scans_mem.store_id =
stores_mem.id
AND
user_stores_disabled_mem.chain =
stores_mem.chain
AND
user_stores_disabled_mem.user_id =
1)
AND ( Sqrt(Pow(111 * (stores_mem.gps_lat
- 40.748080
), 2)
+ Pow(111 * (stores_mem.gps_lng -
-73.990533) *
Cos(
40.748080 /
57.3)
,
2)) <= (SELECT user_mem.distance
FROM user_mem
WHERE id = 1))
AND stores_mem.id = scans_mem.store_id))
UNION ALL
(SELECT item_info_mem.prod_id,
item_info_mem.awaiting_approval,
item_info_mem.prod_name,
item_info_mem.brand,
item_info_mem.size,
item_info_mem.units,
item_info_mem.category,
search_tags_mem.rank,
(SELECT image
FROM images_mem
WHERE prod_id = item_info_mem.prod_id
ORDER BY id
LIMIT 1) AS image,
(SELECT url
FROM urls_mem
WHERE prod_id = item_info_mem.prod_id
AND disabled IS NULL
ORDER BY id DESC
LIMIT 1) AS
url,
(SELECT quantity
FROM shopping_list_mem
WHERE user_id = "1"
AND prod_id = item_info_mem.prod_id
ORDER BY id
LIMIT 1) AS
quantity,
( Substrcount(Lcase(item_info_mem.prod_name), Lcase("bread"))
+ Substrcount(Lcase(item_info_mem.brand), Lcase("bread")) ) AS
word_count,
( Substrcheck(Lcase(item_info_mem.prod_name),
Lcase(item_info_mem.brand), Lcase("bread")) ) AS
word_count_unique
FROM item_info_mem,
search_tags_mem,
scans_mem,
stores_mem
WHERE NOT EXISTS (SELECT id
FROM search_tags_omit_mem
WHERE prod_id = item_info_mem.prod_id
AND tag = "bread"
ORDER BY id DESC
LIMIT 1)
AND scans_mem.price IS NOT NULL
AND scans_mem.expired IS NULL
AND item_info_mem.prod_id = search_tags_mem.prod_id
AND search_tags_mem.tag = "bread"
AND item_info_mem.prod_id = scans_mem.prod_id
AND NOT EXISTS (SELECT user_stores_disabled_mem.user_id
FROM user_stores_disabled_mem,
stores_mem
WHERE scans_mem.store_id = stores_mem.id
AND user_stores_disabled_mem.chain =
stores_mem.chain
AND user_stores_disabled_mem.user_id = 1)
AND ( Sqrt(Pow(111 * (stores_mem.gps_lat - 40.748080), 2)
+ Pow(111 * (stores_mem.gps_lng - -73.990533) * Cos(
40.748080 /
57.3)
,
2)) <= (SELECT user_mem.distance
FROM user_mem
WHERE id = 1))
AND stores_mem.id = scans_mem.store_id)) t3
ORDER BY -rank DESC,
word_count_unique DESC,
word_count DESC,
Field(t3.category, "food", "grocery", "pantry, household & pets",
"confectionery and grocery") DESC,
Length(prod_name),
brand
LIMIT 0, 10
````
There's a lot of reasons why the script being fast or slow.
PC Specs capabilities, DB Server version, DB Structure including indexes, Query structure also with network connection between server and client.
If you are asking Would this be considered slow? with 80k records my answer is NO
This Guide will help you about Query Optimization.

How to group customers into 4 ranks using MYSQL query

We store the customer data like name, userId and Total Amount they spent on different orders they place, now I want to group a customer into ranks 1 to 4 based on Total Amount he spent till now. Below is the script I am using but it takes a lot of time, is there any better way to do this?? There is no index on dateCreate Field.
public function getBiggestSpenders($customerUserId){
global $db, $database;
$sql = "SELECT userId, SUM(total) AS Total, ORD.dateCreate
FROM $database.`order` ORD
WHERE year(ORD.dateCreate) >= '2013'
group by ORD.userId
order by Total DESC";
$result = $db->getTable($sql);
$numRows = count($result);
$flag=0;
for($i=0;$i<$numRows && $flag==0;$i++){
$userId = $result[$i]['userId'];
if($userId==$customerUserId){
$position = $i;
$Total = $result[$i]['Total'];
$flag=1;
}
}
$quartile = $this->getQuartiles($numRows, $position);
if($quartile==1)
return $quartile;
else
return 0;
}
public function getQuartiles($numRows, $position){
$total = $numRows;
$segment = round($total / 4);
$Quartile = floor($position / $segment) + 1;
return $Quartile;
}
Thanks!
To improve the speed, you can create an index on dateCreate column and use the following condition to make MySQL use it:
WHERE ORD.dateCreate >= '2013-01-01'
As far as grouping is concerned, you can use CASE statement to define groups based on spending, e.g.:
SELECT userId, SUM(total) AS Total,
CASE
WHEN Total >= 2000 then 1
WHEN Total >= 1000 AND Total <2000 THEN 2
WHEN Total >=500 AND Total < 1000 THEN 3
ELSE 4
END as `rank`,
ORD.dateCreate
FROM $database.`order` ORD
WHERE ORD.dateCreate >= '2013-01-01'
group by ORD.userId
order by Total DESC

Mysql query with multiple ORDER BY clause and IF conditions

I am trying to query based on these conditions in an orders table,
// This is not an actual query
SELECT *
IF (store_id = 3) THEN ORDER BY WEIGHT DESC,
ELSEIF (STORE_ID = 7) THEN ORDER BY WEIGHT DESC
ELSEIF ORDER BY WEIGHT = DESC
ELSEIF (EQUAL WEIGHT) ORDER BY ORDER_DATE DESC
ELSEIF (EQUAL WEIGHT AND EQUAL ORDER_DATE) ORDER BY STORE_ID DESC
ELSEIF (EQUAL WEIGHT AND EQUAL ORDER_DATE AND EQUAL STORE_ID) ORDER BY
SHIPPING_METHOD DESC
FROM TABLE orders WHERE carrier_name = 'XXX'
This is as far as what I have got,
//Actual query
SELECT id,store_id,weight
FROM orders
WHERE carrier_name = 'XXX'
ORDER BY
IF(store_id = 3,weight,'')DESC,
IF(store_id = 3,order_date,'')DESC,
IF(store_id = 3,store_id,'')DESC,
IF(store_id = 3,shipping_method,'')DESC,
IF(store_id = 7,weight,'')DESC,
IF(store_id = 7,order_date,'')DESC,
IF(store_id = 7,store_id,'')DESC,
IF(store_id = 7,shipping_method,'')DESC,
IF(TRUE,weight,'')DESC,
IF(TRUE,order_date,'')DESC,
IF(TRUE,store_id,'')DESC,
IF(TRUE,shipping_method,'')DESC
The result obtained is
I am not sure why 53.39 and 30.35 are not sorted. What am I doing wrong?
Please suggest if there is a better way to attain this.
This is the required result,
Based on your expected output, the following query should give you what you want:
SELECT *
FROM orders
WHERE carrier_name = 'XXX'
ORDER BY CASE WHEN store_id = 3 THEN 0
WHEN store_id = 7 THEN 1
ELSE 2 END,
store_id,
weight DESC
For an explanation of the logic in the ORDER BY clause, the first condition puts store_id 3 first, followed by 7, followed by everything else. The next conditions orders each of these three groups in ascending order by the store_id. Note that for 3 and 7, this second step will be ignored, because the store_id value is the same for these groups (3 and 7 respectively). Finally, the third condition is to order by the weight in descending order.
ORDER BY
weight DESC, -- as i can see all true in your cases
order_date DESC,
store_id DESC,
shipping_method DESC

Limit query by sum of values

I have a mysql table, transactions, with fields id, transactionType, quantity, price etc. I want to limit records by the cumulative sum of one column. So I want to pull out all the transactions until the cumulative quantity reaches my variable (here <=50).
What am I doing wrong?
SET #qsum := 0;
SELECT *
FROM (
SELECT *, (#qsum := #qsum + quantity) AS cumulative_quantity
FROM transactions ORDER BY id DESC
) transactions
WHERE
transactionType = 'buy'
AND typeID = 10
AND cumulative_quantity <= 50
Try this way
SET #qsum := 0;
SELECT *
FROM (
SELECT *, (#qsum := #qsum + quantity) AS cumulative_quantity
FROM transactions
WHERE transactionType = 'buy'
AND typeID = 10
AND cumulative_quantity <= 50
ORDER BY id DESC
) transaction

SQL: query with complex subqueries

I have the following tables in my game's database:
rankedUp (image_id, user_id, created_at)
globalRank (image_id, rank )
matchups (user_id, image_id1, image_id2)
All image_ids in globalRank table are assigned a rank which is a float from 0 to 1
Assuming I have the current logged in user's "user_id" value, I'm looking for a query that will return a pair of image ids (imageid1, imageid2) such that:
imageid1 has lower rank than imageid2 but is also the next highest rank less than imageid2
matchups table doesn't have (userid,imageid1,imageid2) or (userid,imageid2,imageid1)
rankedup table doesn't have (userid,imageid1) or if it does, the createdat column is older than X hours
What I have so far for requirement 1 is this:
SELECT lowerImages.image_id AS lower_image, higherImages.image_id AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM global_rank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1 , 1
) AS tmp
)
but it doesnt work because I can't reference higherImages.rank in the subquery.
Does anyone know how I could satisfy all of those requirements in one query?
Thanks for your help
EDIT:
I now have this query but I don't know about the efficiency and I need to test it for correctness:
SELECT lowerImages.image_id AS lower_image,
max(higherImages.image_id) AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND 1 NOT IN (select 1 from ranked_up where
lowerImages.image_id = ranked_up.image_id
AND ranked_up.user_id = $user_id
AND ranked_up.created_at > DATE_SUB(NOW(), INTERVAL 1 DAY))
AND 1 NOT IN (
SELECT 1 from matchups where user_id = $userId
AND lower_image_id = lowerImages.image_id
AND higher_image_id = higherImages.image_id
UNION
SELECT 1 from matchups where user_id = $user_id
AND lower_image_id = higherImages.image_id
AND higher_image_id = lowerImages.image_id
)
GROUP BY 1
the "not in" statements I'm using are all indexed so they should run fast. The efficiency problem I have is the group by and selection of the global_rank tables
This question is a revision of Pretty Complex SQL Query, which should no longer be answered.
select
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 1, 1
) as highest,
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 2, 1
) as secondhighest
I normally use SQL Server, but this i think is the translation for mysql :)
This should do the trick:
SELECT lowerImages.*, higherImages.*
FROM globalrank AS lowerImages, globalrank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM globalrank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1,1
) AS tmp
)
AND NOT EXISTS (
SELECT * FROM matchups
WHERE user_id = $user_id
AND ((image_id1 = lowerImages.image_id AND image_id2 = higherImages.image_id)
OR (image_id2 = lowerImages.image_id AND image_id1 = higherImages.image_id))
)
AND higherImages.image_id NOT IN (
SELECT image_id FROM rankedup
WHERE created_at < DATE_ADD(NOW(), INTERVAL 1 DAY)
AND USER_ID <> $user_id
)
ORDER BY higherImages.rank
I'm assuming the PKs of matchups and rankedup include all columns in those tables. This would allow the second 2 sub-queries to utilize the PK indexes. You would probably want an ordered index on globalrank.rank to speed up the first sub-query.