I'll try to detail this the best I can. I have a nested select statement with a where in clause, but the nested part of the select should be interpreted as a literal string (I believe this is the right terminology). However the default behavior of mysql leads to a result I do not want.
I.e.
select class
from cs_item
where code="007"
+-------+
| class |
+-------+
| 1,3 |
+-------+
And the below is a query if I explicitly type "in (1,3)" as part of a select query:
select alpha,description
from cs_quality
where class in (1,3);
+-------+-------------+
| alpha | description |
+-------+-------------+
| STD | STD |
| XS | XS |
| 5 | Sch 5 |
| 10 | Sch 10 |
| 20 | Sch 20 |
| 40 | Sch 40 |
| 60 | Sch 60 |
| 80 | Sch 80 |
| 100 | Sch 100 |
| 120 | Sch 120 |
| 140 | Sch 140 |
| 160 | Sch 160 |
| XXS | XXS |
| 15L | 150# |
| 30L | 300# |
| 40L | 400# |
| 60L | 600# |
| 90L | 900# |
| 150L | 1500# |
| 200L | 2000# |
| 250L | 2500# |
| 300L | 3000# |
| 400L | 4000# |
| 600L | 6000# |
| 900L | 9000# |
+-------+-------------+
But when I go to nest this to get the same result I have...
select alpha,description
from cs_quality
where class in (select class from cs_item where code = "007")
+-------+-------------+
| alpha | description |
+-------+-------------+
| STD | STD |
| XS | XS |
| 5 | Sch 5 |
| 10 | Sch 10 |
| 20 | Sch 20 |
| 40 | Sch 40 |
| 60 | Sch 60 |
| 80 | Sch 80 |
| 100 | Sch 100 |
| 120 | Sch 120 |
| 140 | Sch 140 |
| 160 | Sch 160 |
| XXS | XXS |
+-------+-------------+
Which is just the part of "class in 1"... it balks on the ",3" component. Is there a way for the nested select to be interpreted as literal text?
Thanks all, much appreciated. I had a bit of trouble wording this question but will edit as needed.
Normalize, normalize, normalize your tables, in this case table cs_item. You should NOT store multiple (comma separated) values in one field.
Until you do that, you can use:
select alpha, description
from cs_quality
where FIND_IN_SET( class , (select class from cs_item where code = '007'))
or
select q.alpha, q.description
from cs_quality AS q
join cs_item AS i
on FIND_IN_SET( q.class , i.class )
where i.code = '007'
But this kind of using special functions instead of equality for JOINs, leads to very slow queries. Storing comma separated lists leads to a ton of other problems. See here:
Is storing a comma separated list in a database column really that bad?
Short answer is: Yeah, it's that bad.
Your query needs to return multiple rows like this:
+-------+
| class |
+-------+
| 1 |
+-------+
| 3 |
+-------+
Or else it is as if you are doing:
select alpha,description
from cs_quality
where class in ("1, 3");
Which you do not want.
Better use join, instead of a nested query
Related
I have a table called related_clues which lists the id's of pairs of clues which are related
| id | clue_id | related_clue_id | relatedness |
+----+---------+-----------------+-------------+
| 1 | 1 | 232 | 1 |
| 2 | 1 | 306 | 1 |
| 3 | 1 | 458 | 1 |
| 4 | 2 | 620 | 1 |
| 5 | 2 | 72 | 1 |
| 6 | 3 | 212 | 1 |
| 7 | 3 | 232 | 1 |
| 8 | 3 | 412 | 1 |
| 9 | 3 | 300 | 1 |
+----+---------+-----------------+-------------+
Eventually after a while we may reach two id's such as:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 1 |
| 121331 | 1636 | 38 | 1 |
+--------+---------+-----------------+-------------+
So in this case, for two distinct id values, we have the same (clue_id, related_clue_id) pair
In this case I would like the relatedness value to be updated to 2, signalling that there are two examples of this (clue_id, related_clue_id) pair. Like so:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 2 |
| 121331 | 1636 | 38 | 2 |
+--------+---------+-----------------+-------------+
So essentially I would like to run some SQL that sets the relatedness value to the number of times a (clue_id, related_clue_id) pair appears.
When I have no relatedness column present, and I simply run the SQL:
SELECT id, clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
GROUP BY clue_id, related_clue_id
It gives me the required result, but of course this doesn't store the relatedness column, it simply shows the column if I run this select. So how do I permanently have this relatedness column?
You could use a update with join
Update related_clues a
INNER JOIN (
SELECT clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
group by clue_id, related_clue_id
having count(*) = 2
) t on t.clue_id = a.clue_id
and t.related_clue_id = a.related_clue_id
set a.relatedness = t.relatedness
I would approach this as an update/join but filter out rows that don't need to be updated:
update related_clues rc join
(select clue_id, related_clue_id, COUNT(*) AS cnt
from `related_clues`
group by clue_id, related_clue_id
) t
on t.clue_id = rc.clue_id and
t.related_clue_id = rc.related_clue_id
set rc.relatedness = t.relatedness
where rc.relatedness <> t.relatedness;
my tables and their layout:
mysql> select * FROM xt_shipping_zones;
+---------+-------------+---------------------------------------------------------------------------+
| zone_id | zone_name | zone_countries |
+---------+-------------+---------------------------------------------------------------------------+
| 5 | ZONE1 | AT,BE,BG,DK,FI,FR,GR,IE,IT,LV,LT,LU,MC,NL,PL,PT,RO,SM,SE,SK,SI,ES,HU,GB |
| 6 | Deutschland | DE |
| 8 | ZONE2Brutto | AD,NO,VA |
| 9 | ZONE2NETTO | CH,LI |
+---------+-------------+---------------------------------------------------------------------------+
mysql> select * FROM xt_shipping_cost WHERE shipping_geo_zone = 99995 LIMIT 5;
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
| shipping_cost_id | shipping_id | shipping_geo_zone | shipping_country_code | shipping_type_value_from | shipping_type_value_to | shipping_price | shipping_allowed |
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
| 269 | 34 | 99995 | | 0.31 | 17.99 | 17.0000 | 1 |
| 270 | 34 | 99995 | | 17.99 | 35.99 | 34.0000 | 1 |
| 271 | 34 | 99995 | | 35.99 | 53.99 | 51.0000 | 1 |
| 272 | 34 | 99995 | | 53.99 | 71.99 | 68.0000 | 1 |
| 273 | 34 | 99995 | | 71.99 | 89.99 | 85.0000 | 1 |
+------------------+-------------+-------------------+-----------------------+--------------------------+------------------------+----------------+------------------+
mysql> SELECT * FROM geoip WHERE 92569600 BETWEEN start AND end;
+----------+----------+---------+-----+
| start | end | country | id |
+----------+----------+---------+-----+
| 92569600 | 92585983 | AT | 895 |
+----------+----------+---------+-----+
My Query:
SELECT
xt_shipping_cost.shipping_type_value_from,
xt_shipping_cost.shipping_type_value_to,
xt_shipping_cost.shipping_price,
geoip.country
FROM xt_shipping_cost
INNER JOIN xt_shipping_zones
ON xt_shipping_cost.shipping_geo_zone = xt_shipping_zones.zone_id + 99990
INNER JOIN geoip
ON geoip.country REGEXP xt_shipping_zones.zone_countries
WHERE 34664448 BETWEEN geoip.start AND geoip.end
My Problem:
Query is working if there is only ONE entry in xt_shipping_zones.zone_countries like DE. If there are multiple (with comma seperated entries) i cant get a match on that row.
Doing it manually:
mysql> SELECT * FROM `xt_shipping_zones` WHERE `zone_countries` REGEXP 'AT';
+---------+-----------+---------------------------------------------------------------------------+
| zone_id | zone_name | zone_countries |
+---------+-----------+---------------------------------------------------------------------------+
| 5 | ZONE1 | AT,BE,BG,DK,FI,FR,GR,IE,IT,LV,LT,LU,MC,NL,PL,PT,RO,SM,SE,SK,SI,ES,HU,GB |
+---------+-----------+---------------------------------------------------------------------------+
SQLFiddle: http://sqlfiddle.com/#!9/68f8d0/1
I hope i didn't failed to much to make my problem clear.
Thank you
I think you can use find_in_set()
SELECT
xt_shipping_cost.shipping_type_value_from,
xt_shipping_cost.shipping_type_value_to,
xt_shipping_cost.shipping_price,
geoip.country
FROM xt_shipping_cost
INNER JOIN xt_shipping_zones
ON xt_shipping_cost.shipping_geo_zone = xt_shipping_zones.zone_id + 99990
INNER JOIN geoip
ON find_in_set(geoip.country, xt_shipping_zones.zone_countries)
WHERE 34664448 BETWEEN geoip.start AND geoip.end
It is no good idea to store the values as csv. That is very bad database design.
mysql> select * from fact_lab;
+---------+--------+-----+
| product | amount | box |
+---------+--------+-----+
| a | 100 | 1 |
| b | 200 | 1 |
| c | 50 | 1 |
| a | 200 | 2 |
| b | 100 | 2 |
| c | 50 | 2 |
| a | 100 | 3 |
| b | 200 | 3 |
| c | 50 | 3 |
+---------+--------+-----+
9 rows in set (0.00 sec)
I am looking for an output where I can see the total sum of amounts for each product that will show a comparison with amounts for box 2. So, the output should be like the below
+---------+--------+-----+
| product | amount | inbox2 |
+---------+--------+-----+
| a | 400 | 200 |
| b | 500 | 100 |
| c | 150 | 50 |
+---------+--------+-----+
How can i get this result in a single query?
You can get what you want with aggregation. The group by is a basic part of the SQL language. If you don't understand it, then you should study up a bit more on the language.
The second part uses condition aggregation. That is, a case statement is the argument to sum():
select fl.product, sum(amount) as amount,
sum(case when box = 2 then amount else 0 end) as inbox2
from fact_lab fl
group by fl.product;
I have 3 tables , structures are given below
table_incident
+-------+-------------+------------+-----------------------------------+
| id(PK) | incident_display_id | account_id | customized_fields_id |
+-------+-------------+------------+-----------------------------------+
| 47614 | 33 | 394 | 1285,1286,1287,1288 |
+-------+-------------+------------+-----------------------------------+
table_customized_fields_data
+------+------------+----------+-------------+--------------------+
| id | account_id | field_id(FK) | incident_id(FK) | value |
+------+------------+----------+-------------+--------------------+
| 1285 | 394 | 49 | 47614 | Nikon 5MP |
| 1286 | 394 | 50 | 47614 | CDMA |
| 1287 | 394 | 51 | 47614 | Yes |
| 1288 | 394 | 84 | 47614 | 9317001007 |
+------+------------+----------+-------------+--------------------+
table_customized_fields
+----+------------+------------+---------------------+------+-------------+
| id | account_id | field_type | label | name | field_lable |
+----+------------+------------+---------------------+------+-------------+
| 49 | 394 | text_field | Camera | | |
| 50 | 394 | checkbox | Cellphone | | CDMA, GSM |
| 51 | 394 | radio | Sunglasses | | Yes, No |
| 52 | 394 | textarea | Credit Card | | 5 |
| 83 | 394 | radio | Cowboy Hat | | Yes,No |
| 84 | 394 | text_field | Emergency Contact # | | |
+----+------------+------------+---------------------+------+-------------+
Now i want to select only those label and regarding value of that lable which exists in table_incident,
i fired below query
SELECT ti.id IncID,tcf.id labelID,tcfd.id dataID,tcf.label, tcfd.value
FROM table_customized_fields_data tcfd
INNER JOIN table_incident ti ON (ti.id = tcfd.incident_id)
INNER JOIN table_customized_fields tcf ON (tcf.id = tcfd.field_id)
WHERE tcfd.id IN (ti.customized_fields_id)
AND ti.id=47614
+-------+---------+--------+--------+-----------+
| IncID | labelID | dataID | label | value |
+-------+---------+--------+--------+-----------+
| 47614 | 49 | 1285 | Camera | Nikon 5MP |
+-------+---------+--------+--------+-----------+
but only one row is returned, will you all please tell me what wrong i m doing though each subquery working perfect individually.
Note: although this query retruns the desired data:
SELECT tcf.id AS labelID,tcf.label,tcfd.id AS dataID, tcfd.value
FROM table_customized_fields_data tcfd
JOIN table_customized_fields tcf ON tcf.id=tcfd.field_id
JOIN table_incident ti ON ti.id=tcfd.incident_id
WHERE ti.id=47614
but i think there must be some optimized way, please share your idea.
Thanks
Your second query IS the optimized way. Just make sure you have an index on table_customized_fields_data.incident_id.
Also I think you have misunderstood how IN works, quote from the manual:
expr IN (value,...)
Returns 1 if expr is equal to any of the values in the IN list, else returns 0...
so your
tcfd.id IN (ti.customized_fields_id)
is equivalent to:
tcfd.id = ti.customized_fields_id
ti.customized_fields_id is a string, not an array. It's not matching, but you're getting one row because it's a left join and it's mysql.
What you want is something like this:
SELECT ti.id IncID,tcf.id labelID,tcfd.id dataID,tcf.label, tcfd.value
FROM table_customized_fields_data tcfd
INNER JOIN table_incident ti ON ti.id = tcfd.incident_id
AND concat(',', customized_fields_id, ',') like concat('%,', tcfd.id, ',%')
INNER JOIN table_customized_fields tcf ON tcf.id = tcfd.field_id
WHERE ti.id=47614
which says tcfd.id is in the string customized_fields_id when delimited by commas
+--------------------+---------------+------+-----+---------+-------+
| ID | GKEY |GOODS | PRI | COUNTRY | Extra |
+--------------------+---------------+------+-----+---------+-------+
| 1 | BOOK-1 | 1 | 10 | | |
| 2 | PHONE-1 | 2 | 12 | | |
| 3 | BOOK-2 | 1 | 13 | | |
| 4 | BOOK-3 | 1 | 10 | | |
| 5 | PHONE-2 | 2 | 10 | | |
| 6 | PHONE-3 | 2 | 20 | | |
| 7 | BOOK-10 | 2 | 20 | | |
| 8 | BOOK-11 | 2 | 20 | | |
| 9 | BOOK-20 | 2 | 20 | | |
| 10 | BOOK-21 | 2 | 20 | | |
| 11 | PHONE-30 | 2 | 20 | | |
+--------------------+---------------+------+-----+---------+-------+
Above is my table. I want to get all records which GKEY > BOOK-2, Who can tell me the expression with mysql?
Using " WHERE GKEY>'BOOK-2' " Cannot get the correct results.
How about (something like):
(this is MSSQL - I guess it will be similar in MySQL)
select
*
from
(
select
*,
index = convert(int,replace(GKEY,'BOOK-',''))
from table
where
GKEY like 'BOOK%'
) sub
where
sub.index > 2
By way of explanation: The inner query basically recreates your table, but only for BOOK rows, and with an extra column containing the index in the right data type to make a greater than comparison work numerically.
Alternatively something like this:
select
*
from table
where
(
case
when GKEY like 'BOOK%' then
case when convert(int,replace(GKEY,'BOOK-','')) > 2 then 1
else 0
end
else 0
end
) = 1
Essentially the problem is that you need to check for BOOK before you turn the index into a numberic, as the other values of GKEY would create an error (without doing some clunky string handling).
SELECT * FROM `table` AS `t1` WHERE `t1`.`id` > (SELECT `id` FROM `table` AS `t2` WHERE `t2`.`GKEY`='BOOK-2' LIMIT 1)