MySQL returns bad result - mysql

I have question about SELECT FROM WHERE statement, which returns me bad result.
Here is my table called friends:
+----------+-----------+------------+--------+--------+-------+
| lastname | firstname | callprefix | phone | region | zip |
+----------+-----------+------------+--------+--------+-------+
| Lužný | Bob | 602 | 111222 | OL | 79821 |
| Matyáš | Bob | 773 | 123456 | BR | NULL |
| Strouhal | Fido | 300 | 343434 | ZL | 76701 |
| Přikryl | Tom | 581 | 010101 | PL | 72000 |
| Černý | Franta | 777 | 000999 | OL | 79801 |
| Zavadil | Olda | 911 | 111311 | OL | 79604 |
| Berka | Standa | 604 | 111234 | ZL | 72801 |
| Vlcik | BbB | 736 | 555444 | KV | 35210 |
+----------+-----------+------------+--------+--------+-------+
And here is my query.
SELECT * FROM friends WHERE region <= 'z';
I would expect that the rows with region ZL should be present, but they are not. Can you please tell me why?
Result is:
+----------+-----------+------------+--------+--------+-------+
| lastname | firstname | callprefix | phone | region | zip |
+----------+-----------+------------+--------+--------+-------+
| Lužný | Bob | 602 | 111222 | OL | 79821 |
| Matyáš | Bob | 773 | 123456 | BR | NULL |
| Přikryl | Tom | 581 | 010101 | PL | 72000 |
| Černý | Franta | 777 | 000999 | OL | 79801 |
| Zavadil | Olda | 911 | 111311 | OL | 79604 |
| Vlcik | BbB | 736 | 555444 | KV | 35210 |
+----------+-----------+------------+--------+--------+-------+
When I try this query:
SELECT * FROM friends WHERE region >= 'z';
the result contains both rows with region = 'ZL'
????
Thank you!

Because "ZL" is greater than "Z." Z is just one character so will only return values less that Z or with the value of Z. What are you trying to achieve with this query?

Can you please tell me why?
If you add a record where region is Z, and sorted those rows alphabetically by region, would you expect ZL to come before or after Z? Obviously it would come after, so it does not meet your criteria.
If you want to only consider the first character, then add that to your criteria:
SELECT * FROM friends WHERE LEFT(region,1) <= 'Z';
I would also make Z explicitly a capital letter in case your database settings make it a case-sensitive search.

Have you tried
SELECT * FROM friends WHERE region <= 'zl';?
From the computer's perspective, 'z' < 'zl'

Related

Replace < 1 values in an aggregate SUM T-SQL query

I am try to avoid using #temp to get an average where a field from table contains just a few less than 1 values. Convert to varchar, replace then convert back seems ugly. Suggestions very welcomed!!
Select
(select cmp_zip from company where cmp_id = ord_originpoint) as OriginZip,
ord_originpoint as OriginPoint,
replace((select cty_nmstct from company where cmp_id = ord_originpoint),'/','') as Origin_City_State,
(select cmp_zip from company where cmp_id = ord_destpoint) as DestZip,
ord_destpoint as DestPoint,
replace((select cty_nmstct from company where cmp_id = ord_destpoint),'/','') as Dest_City_State,
COUNT(ord_hdrnumber) as OrdCount,
SUM(ord_rate)/COUNT(ord_hdrnumber) as AvgRate,
SUM(ord_totalmiles)/COUNT(ord_hdrnumber) as AvgMiles,
(SUM(ord_rate) / COUNT(ord_hdrnumber)) / (SUM(ord_totalmiles) / COUNT(ord_hdrnumber)) as AvgRevperMiles
from
orderheader (NOLOCK)
where ord_billto <> 'CSXJAC01'
and ord_revtype1 = 'NE'
and ord_status = 'CMP'
and ord_bookdate > GETDATE() - 730
and ord_completiondate < GETDATE()
and ord_totalmiles > 0
group by
ord_originpoint,ord_destpoint
order by
OrdCount desc
If I exclude columns less than zero in the totalmiles column I get :
+-------+----------+---------------+-------+-------+----------+-----------------+-------+---+------+-----+
| 8850 | DSDMIL | MILLTOWN | NJMID | 7206 | NORELI | ELIZABETH | NJ | 1 | 243 | 25 |
| 7047 | CSXNOR | NORTH BERGEN | NJ | 11550 | NASHEM | HEMPSTEAD | NYNAS | 1 | 492 | 34 |
| 7047 | CSXNOR | NORTH BERGEN | NJ | 7022 | PERFAI | FAIRVIEW | NJBER | 1 | 190 | 1 |
| 17013 | PEPCAR01 | CARLISLE | PA | 21224 | CSXBAL | BALTIMORE | MD | 1 | 350 | 97 |
| 23944 | GARKEN | KENBRIDGE | VA | 21224 | CSXBAL | BALTIMORE | MD | 1 | 814 | 230 |
| 21224 | CSXBAL | BALTIMORE | MD | 18202 | HAZHAZ04 | HAZLETON | PA | 1 | 621 | 161 |
| 17055 | WOOMEC | MECHANICSBURG | PACUM | 21224 | CSXBAL | BALTIMORE | MD | 1 | 355 | 85 |
| 23139 | MORPOW01 | POWHATAN | VA | 21224 | CSXBAL | BALTIMORE | MD | 1 | 1376 | 186 |
| 17109 | PEPHAR | HARRISBURG | PA | 21224 | CSXBAL | BALTIMORE | MD | 1 | 350 | 78 |
| 21224 | CSXBAL | BALTIMORE | MD | 20066 | WASWAS05 | WASHINGTON | DC | 1 | 675 | 54 |
| 21224 | CSXBAL | BALTIMORE | MD | 20743 | GRACAP | CAPITOL HEIGHTS | MD | 1 | 300 | 45 |
| 7047 | CSXNOR | NORTH BERGEN | NJ | 7866 | ROCROC05 | ROCKAWAY | NJ | 1 | 243 | 34 |
| 21224 | CSXBAL | BALTIMORE | MD | 20772 | BOBUPP | UPPER MARLBORO | MD | 1 | 283 | 37 |
+-------+----------+---------------+-------+-------+----------+-----------------+-------+---+------+-----+
I'm still not entirely sure of the exact question that you're asking, but the way that you're doing subqueries within your SELECT statement is always going to be poor for performance, try using correct joins, something like this;
SELECT
c1.cmp_zip AS OriginZip
,oh.ord_originpoint AS OriginPoint
,replace(c1.cty_nmstct),'/','') AS Origin_City_State
,c2.cmp_zip AS DestZip
,oh.ord_destpoint AS DestPoint
,replace(c2.cty_nmstct,'/','') AS Dest_City_State
,COUNT(oh.ord_hdrnumber) AS OrdCount
,SUM(oh.ord_rate)/COUNT(oh.ord_hdrnumber) AS AvgRate
,SUM(oh.ord_totalmiles)/COUNT(oh.ord_hdrnumber) AS AvgMiles
,(SUM(oh.rd_rate)/COUNT(oh.ord_hdrnumber))/( SUM(oh.ord_totalmiles)/COUNT(oh.ord_hdrnumber)) AS AvgRevperMiles
FROM orderheader oh
LEFT JOIN company c1
ON oh.ord_originpoint = c1.cmp_id
LEFT JOIN company c2
ON oh.ord_destpoint = c2.cmp_id
WHERE oh.ord_billto <> 'CSXJAC01'
AND oh.ord_revtype1 = 'NE'
AND oh.ord_status = 'CMP'
AND oh.ord_bookdate > GETDATE() - 730
AND oh.ord_completiondate < GETDATE()
AND oh.ord_totalmiles > 0
GROUP BY
c1.cmp_zip
,oh.ord_originpoint
,replace(c1.cty_nmstct),'/','')
,c2.cmp_zip
,oh.ord_destpoint
,replace(c2.cty_nmstct,'/','')
ORDER BY OrdCount DESC
You may need to tweak the odd field (i'm not sure the table that contains the field rd_rate for example, althouth this may be a typo for ord_rate at a guess)

mysql: comparing two columns

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.

Compare different rows and bring out result

I have a table which requires me to pair certain rows together using a unique value that both the rows share.
For instance in the below table;
+--------+----------+-----------+-----------+----------------+-------------+
| id | type | member | code | description | matching |
+--------+----------+-----------+-----------+----------------+-------------+
| 1000 |transfer | 552123 | SC120314 | From Gold | |
| 1001 |transfer | 552123 | SC120314 | To Platinum | |
| 1002 |transfer | 833612 | SC120314 | From silver | |
| 1003 |transfer | 833612 | SC120314 | To basic | |
| 1004 |transfer | 457114 | SC150314 | From Platinum | |
| 1005 |transfer | 457114 | SC150314 | To silver | |
| 1006 |transfer | 933276 | SC180314 | From Gold | |
| 1007 |transfer | 933276 | SC180314 | From To basic | |
+--------+----------+-----------+-----------+----------------+-------------+
basically What i need the query / routine to do is find the rows where the value in the 'member' column for each row match. Then see if the values in the 'code' column for the same found rows also match.
If both columns for both rows match, then assign a value to the 'matching' column for both rows. This value should be the same for both rows and unique to only them.
The unique code can be absolutely anything, so long as it's exclusive to matching rows. Is there any query / routine capable of carrying this out?
I'm not sure I understand the question correctly, but if you like to pick out and update rows where the code and member columns matches and set matching to some unique value for each of the related rows, I believe this would work:
UPDATE <table> A
INNER JOIN (SELECT * FROM <table>) B ON
B.member = A.member && B.code = A.code && A.id <> B.id
SET A.matching = (A.id + B.id);
The matching value will be set to the sum of the id columns for both rows. Notice that updating the matching field this way will not work if there are more than two rows that can match.
Running the above query against your example table would yield:
+------+----------+--------+----------+---------------+----------+
| id | type | member | code | description | matching |
+------+----------+--------+----------+---------------+----------+
| 1000 | transfer | 552123 | SC120314 | From Gold | 2001 |
| 1001 | transfer | 552123 | SC120314 | To Platinum | 2001 |
| 1002 | transfer | 833612 | SC120314 | From Silver | 2005 |
| 1003 | transfer | 833612 | SC120314 | To basic | 2005 |
| 1004 | transfer | 457114 | SC150314 | From Platinum | 2009 |
| 1005 | transfer | 457114 | SC150314 | To silver | 2009 |
| 1006 | transfer | 933276 | SC180314 | From Gold | 2013 |
| 1007 | transfer | 933276 | SC180314 | From To basic | 2013 |
+------+----------+--------+----------+---------------+----------+
I can give you a simple query what can do what you need.
tst is the name of the table.
SELECT *, COUNT( t2.id ) as matching FROM tst t LEFT JOIN tst t2 ON t2.member = t.member GROUP BY t.id

MySQL - Use Header Name as Part of Query Filter

I'm relatively new to MySQL and have come across a problem to which I cannot seem to find a solution. I have searched but could not find an answer. I'm open to the possibility that I'm not asking the question correctly. Here goes:
I'm trying to use the name of a given column and the values within that column from one table to pull values from another table. The first table contains 3 columns with the response codified. The second table contains the definitions for each code for each item. The same number code is associated with different meanings depending on the item. For example:
table1 (this table cannot change):
--------------------------------------------------------------
|result_id | f_initial | l_name | item_A | item_B | item_C |
--------------------------------------------------------------
| 1 | j | doe | 1 | 3 | 2 |
| 2 | k | smith | 3 | 1 | 2 |
| 3 | l | williams | 2 | 2 | 1 |
--------------------------------------------------------------
table2 (this table can be modified, split, or whatever needs to be done):
-------------------------------------------
|item_id | item_name | score | definition |
-------------------------------------------
| 1 | item_A | 1 | agree |
| 2 | item_A | 2 | neutral |
| 3 | item_A | 3 | disagree |
| 4 | item_B | 1 | likely |
| 5 | item_B | 2 | not likely |
| 6 | item_B | 3 | no reply |
| 7 | item_C | 1 | yes |
| 8 | item_C | 2 | no |
-------------------------------------------
My goal is for the query to output the following:
--------------------------------------------------------------------
|result_id | f_initial | l_name | item_A | item_B | item_C |
--------------------------------------------------------------------
| 1 | j | doe | agree | no reply | no |
| 2 | k | smith | disagree | likely | no |
| 3 | l | williams | neutral | not likely | yes |
--------------------------------------------------------------------
Any assistance or guidance is greatly appreciated. Thank you in advance.
You must join the two tables on the item_A/B/C and score columns
select t1.result_id, t1.f_initial, t1.l_name,
t2a.definition as item_a,
t2b.definition as item_b,
t2c.definition as item_c
from table1 t1
join table2 t2a on t2a.score = t1.item_a
join table2 t2b on t2b.score = t1.item_b
join table2 t2c on t2c.score = t1.item_c
where t2a.item_name = 'item_A'
and t2b.item_name = 'item_B'
and t2c.item_name = 'item_C'

MySQL IN return only one rows while JOIN query return desired result?

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