I am trying to apply a conditional condition inside ON clause of a LEFT JOIN. What I am trying to achieve is somewhat like this:
Pseudo Code
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND (some condition)
AND (
IF (s.type = 0 AND s.code = 'me')
ELSEIF (s.type = 1 AND s.code = 'my-group')
ELSEIF (s.type = 2)
)
I want the query to return the row, if it matches any one of the conditions (Edit: and if it matches one, should omit the rest for the same item).
Sample Data
Sales
item_no | type | code | price
1 0 me 10
1 1 my-group 12
1 2 14
2 1 my-group 20
2 2 22
3 2 30
4 0 not-me 40
I want the query to return
item_no | type | code | price
1 0 me 10
2 1 my-group 20
3 2 30
Edit: The sales is table is used to apply special prices for individual users, user groups, and/or all users.
if type = 0, code contains username. (for a single user)
if type = 1, code contains user-group. (for users in a group)
if type = 2, code contains empty-string (for all users).
Use the following SQL (assumed, the the table sales has a unique id field as usual in yii):
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND id = (
SELECT id FROM sales
WHERE item_no = i.sku
AND (type = 0 AND code = 'me' OR
type = 1 AND code = 'my-group' OR
type = 2)
ORDER BY type
LIMIT 1
)
Try following -
SELECT *,SUBSTRING_INDEX(GROUP_CONCAT(s.type ORDER BY s.type),','1) AS `type`, SUBSTRING_INDEX(GROUP_CONCAT(s.code ORDER BY s.type),','1) AS `code`,SUBSTRING_INDEX(GROUP_CONCAT(s.price ORDER BY s.type),','1) AS `price`
FROM item AS i
LEFT JOIN sales AS s
ON i.sku = s.item_no AND (SOME CONDITION)
GROUP BY i.sku
Related
So I'm trying to do something that I think should be fairly simple with SQL. But I'm having a hard time figuring it out. Here is the format of my data:
One table with user information, let's call it User:
ID name_user Drive_Type
1 Tim Stick shift
2 Jim Automatic
3 Bob Automatic
4 Lisa Stick shift
Then I have one table used for the join, let's call it Join_bridge:
user_ID car_has_ID
1 12
2 13
3 14
4 14
And one table with car information, let's call it Car:
car_ID name
12 Honda
13 Toyota
14 Ford
Then what I want is something that looks like this with the total number of Ford's that are stick shift and the percentage
name Total percentage
Ford 1 25%
I have tried the following, which gets the total right, but not the percentage:
select Drive_Type,
name,
count(Drive_Type) as Total,
(count(Drive_Type) / (select count(*)
from User
join Join_bridge
on User.ID = user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
) * 100.0 as Percent
from User
join Join_bridge
on User.ID = Join_bridge.user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
where name = 'Ford' and Drive_Type = "Automatic"
;
What am I missing? Thanks.
See this SQL Fiddle with the query - the trick is to SUM over CASE that returns 1 for rows you look for and 0 for the rest in order to calculate "Total" at the same time you can also count all rows to calculate percentage.
Here's the SQL query:
SELECT
'Ford' name,
SUM(a.ford_with_stack_flag) Total,
100.0 * SUM(a.ford_with_stack_flag) / COUNT(*) percentage
FROM (
SELECT
Car.name,
(CASE WHEN User.Drive_Type = 'Stick Shift' and Car.name = 'Ford' THEN 1 ELSE 0 END) ford_with_stack_flag
FROM User
JOIN Join_bridge on User.ID = Join_bridge.user_ID
JOIN Car ON Car.car_ID = Join_bridge.car_has_ID
) a
Compute percent and join to Car. Window functions are supported in MySql 8.0
select c.car_ID, c.name, p.cnt, p.Percent
from car c
join (
select car_has_ID, u.Drive_Type,
count(*) cnt,
count(*) / count(count(*)) over() Percent
from Join_bridge b
join user u on u.ID = b.user_ID
group by b.car_has_ID, u.Drive_Type
) p on p.car_has_ID = c.car_ID
where c.name = 'Ford' and p.Drive_Type='Stick shift';
db<>fiddle
I am about to a build a notification feature
The app is a car ads website
The dealer inserts car ads
The visitor Could save searches as string (URL)
---------------------------------------------------------
saved_search_id|visitor_id |search_url
---------------------------------------------------------
0 | 1 |type=0&price_max=10000&color=red
1 | 1 |type=2&price_max=15000&color=black
2 | 2 |type=3&price_max=20000&color=white
Whene the dealer inserts a new car, i parse all saved searches into SQL queries
//array(arrays(saved_search_id, saved_search_query))
array(
array(0, "EXISTS(SLECT car_id FROM Car WHERE type=0 AND price <= 10000 AND color = red)"),
array(1, "EXISTS(SLECT car_id FROM Car WHERE type=2 AND price <= 15000 AND color = black)"),
array(2, "EXISTS(SLECT car_id FROM Car WHERE type=3 AND price <= 20000 AND color = white)")
)
For each saved_search_query i check Whether the new car is included in search result or not. if yes, i send an Email to notify the visitor
i can't figure out how to build one query that returns relevant saved_search_id … instead of running all queries one by one (thousands of Saved searches)
Below is the closest expression to what i am trying to translate
CREATE FUNCTION get_saved_search_id(query, id){
if(query){
return id;
}
}
SELECT get_saved_search_id('EXISTS(SLECT car_id FROM Car WHERE type=0 AND price <= 10000 AND color = red)', 0)
UNION
SELECT get_saved_search_id('EXISTS(SLECT car_id FROM Car WHERE type=2 AND price <= 15000 AND color = black) ', 1)
UNION
SELECT get_saved_search_id('EXISTS(SLECT car_id FROM Car WHERE type=3 AND price <= 20000 AND color = white)', 2)
You could potentially do it by using a CROSS JOIN and generating a humongous WHERE/OR clause (instead of your EXISTS), with one condition for each saved_search_id, as follows:
SELECT saved_search_id,
visitor_id,
car_id
FROM searches a
CROSS JOIN cars b
-- generated WHERE clause below based on saved_search_id + search_url column
WHERE (saved_search_id = 0 AND type = 0 AND price <= 10000 AND color = 'red')
OR (saved_search_id = 1 AND type = 2 AND price <= 15000 AND color = 'black')
OR (saved_search_id = 2 AND type = 3 AND price <= 20000 AND color = 'white')
EDIT: add in a filter on inserted car id (10, for example)
SELECT saved_search_id,
visitor_id,
car_id
FROM searches a
CROSS JOIN cars b
-- generated WHERE clause below based on saved_search_id + search_url column
WHERE (
(saved_search_id = 0 AND type = 0 AND price <= 10000 AND color = 'red')
OR (saved_search_id = 1 AND type = 2 AND price <= 15000 AND color = 'black')
OR (saved_search_id = 2 AND type = 3 AND price <= 20000 AND color = 'white')
)
AND car_id = 10 --<-- inserted car id
The dealer inserts a new car with particular #type, #price, and #color. Use these parameters to find the related searches:
select *
from searches
where search_url like concat('type=', #type, '%color=', #color)
and cast(substring_index(search_url, '&price_max=', -1) as int) >= #price;
(Casting to integer in MySQL takes the number only and ignores the rest of the string.)
And if you want to use the already inserted car row instead:
select *
from searches s
where exists
(
select null
from cars c
where c.id = 12345 -- the inserted row's ID
and s.search_url like concat('type=', c.type, '%color=', c.color)
and cast(substring_index(s.search_url, '&price_max=', -1) as int) >= c.price
);
Of course you can also write a function for this accepting the search string and the car parameters - or the car ID for an already inserted car row. With the latter you'd have something like:
select *
from searches s
where search_matches_car(s.search_url, 12345);
The same with a join for the case you want to see car information, too:
select *
from cars c
join searches s on search_matches_car(s.search_url, c.id)
where c.id = 12345;
Each row in the assets table contains the fields value and type. The type field is a relation ship to the types table where each row has a field is_negative:
// assets
id | value | type
----------------------
1 | 10 | 1
2 | 4 | 2
3 | 1 | NULL
// types
id | is_negative
------------------
1 | 0
2 | 1
I would like to query the sum of all asset values where the type specifies whether the value is negative or positive. Assets with no type should have a negative value.
In the above example the result should be 10 - 4 - 1 = 5
Is this somehow possible within a single SELECT SUM(value)... statement?
A tricky way (avoiding conditions):
select sum( value * (0.5 - coalesce(t.is_negative, 1)) * 2 )
from assets a
left join types t on t.id = a.type
Another way (more readable):
select sum(value * case when not is_negative then 1 else -1 end)
from assets a
left join types t on t.id = a.type;
Yes, quite easily.
SELECT SUM(IF(is_negative = 1 OR is_negative IS NULL, value * -1, value))
FROM assets a
LEFT JOIN types t ON a.type = t.id
...or very similarly...
SELECT SUM(CASE WHEN COALESCE(is_negative,1)= 1 THEN value * -1 ELSE value END) x
FROM assets x
LEFT
JOIN types y
ON y.id = x.type;
situation:
table 1 - #__virtuemart_products
virtuemart_product_id | product_special
PRODUCTS_IDS | 0 or 1
table 2 - #__virtuemart_product_badges
virtuemart_product_id | product_badge
PRODUCTS_IDS | for this situation code 3
I have a default SQL
SELECT p.`virtuemart_product_id`
FROM `#__virtuemart_products` as p
WHERE p.`product_special` = 1;
results is product IDs like 2,3,225,...
I need modify this SQL syntax for select IDs from 2 different tables and return one column.
If I modify syntax like that:
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
Result is:
virtuemart_product_id | virtuemart_product_id
1 | 123
1 | 321
1 | 231
....
why is first column 1,1,1,...? here must be product_id, no product_special code
I need group this results into one column virtuemart_product_id
What I doing wrong?
I think what you are looking for is UNION of the IDs fetched from two different tables.
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as
badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
What the above query is doing is, it is performing a join between the two tables with the condition that product_special should be 1 or badge should be 3. Hence, each row from one table will be joined with each row of the other table where the condition will satisfy.
To get IDs from both the tables you can get the results from each table according to condition and then perform a UNION on them. So for example
(SELECT `virtuemart_product_id` FROM `#__virtuemart_products` WHERE
`product_special` = 1)
UNION
(SELECT `virtuemart_product_id` FROM
`#__virtuemart_product_badges` WHERE `badge` = 3)
I hope this helps.
I have rows of data from a SELECT query with a few prices (say three for this example). One is our price, one is competitor1 price, one is competitor2 price. I want to add a column that spits out the rank of our price as compared to the other two prices; if our price is the lowest it would spit out the number 1 if the highest it would spit out the number it is out of.
Something like this:
Make | Model | OurPrice | Comp1Price | Comp2Price | Rank | OutOf
MFG1 MODEL1 350 100 500 2 3
MFG1 MODEL2 50 100 100 1 3
MFG2 MODEL1 100 NULL 50 2 2
MFG2 MODEL2 9999 500 NULL 2 2
-Sometimes the competitor price will be NULL as seen above, and I believe this is where my issue lies. I have tried a CASE and it works when only on one competitor but when I add a AND statement it spits out the ranks as all NULL. Is there a better way of doing this through a MySQL query?
SELECT
MT.MAKE as Make,
MT.MODEL as Model,
MT.PRICE as OurPrice,
CT1.PRICE as Comp1Price,
CT2.PRICE as Comp2Price,
CASE
WHEN MT.PRICE < CT1.PRICE AND MT.PRICE < CT2.PRICE
THEN 1 END AS Rank
(CT1.PRICE IS NOT NULL) + (CT2.PRICE IS NOT NULL) + 1 as OutOf
FROM mytable MT
LEFT JOIN competitor1table as CT1 ON CT1.MODEL = MT.MODEL
LEFT JOIN competitor2table as CT2 ON CT2.MODEL = MT.MODEL
ORDER BY CLASS
Not tested, but you can try:
SELECT
a.MAKE AS Make,
a.MODEL AS Model,
a.PRICE AS OurPrice
MAX(CASE WHEN a.compnum = 1 THEN pricelist END) AS Comp1Price,
MAX(CASE WHEN a.compnum = 2 THEN pricelist END) AS Comp2Price,
FIND_IN_SET(a.PRICE, GROUP_CONCAT(a.pricelist ORDER BY a.pricelist)) AS Rank,
COUNT(a.pricelist) AS OutOf
FROM
(
SELECT MAKE, MODEL, PRICE, PRICE AS pricelist, 0 AS compnum
FROM mytable
UNION ALL
SELECT a.MAKE, a.MODEL, a.PRICE, CT1.PRICE, 1
FROM mytable a
LEFT JOIN competitor1table CT1 ON a.MODEL = CT1.MODEL
UNION ALL
SELECT a.MAKE, a.MODEL, a.PRICE, CT2.PRICE, 2
FROM mytable a
LEFT JOIN competitor2table CT2 ON a.MODEL = CT2.MODEL
) a
GROUP BY
a.MAKE, a.MODEL
(CT1.PRICE IS NOT NULL AND CT1.PRICE < MT.PRICE) + (CT2.PRICE IS NOT NULL AND CT2.PRICE < MT.PRICE) + 1 as Rank