I've got 5 columns in table:
- id / a /b /operation/ c
where for example: 1 / 2 /3 / + / 5
What is my goal is to check if operation (+/-/*/%) is correct with the formula (a (operation) b = c) and show only those records where formula is true.
Honestly I've been trying to solve that but unfortunately I really don't know how to...
For sure I have to start with operation column:
(IF(operation = '+', a+b, IF( operation = '-', a-b, IF(operation = '*', a*b, IF(operation = '/',a/b, ''))))
Shall I mix it with IF CASE maybe? What's more, zero exepction needs to be added
Use case:
select (c = (case when operation = '+' then a + b
when operation = '-' then a - b
when operation = '/' then a / nullif(b, 0)
. . .
end)
) as expected_equals_actual
Related
I have the query outlined as below. At present it takes over 8 min to run given there are over 8 million records within the zt_Arrival_Data table, while the zt_Tpl_Tuple_Stats_2 tale only carries 9774 records, with the total output of simply 6946 unique records.
In what way can I structure this query to improve performance?
SELECT distinct b.Tuple_ID
, LTRIM(RTRIM(a.ORIGIN_CITY)) + ', ' + LTRIM(RTRIM(a.ORIGIN_STATE)) AS Origin_TX
, LTRIM(RTRIM(a.DESTINATION_CITY)) + ', ' + LTRIM(RTRIM(a.DESTINATION_STATE)) AS Destination_TX
, LTRIM(RTRIM(a.ORIGIN_CITY)) + ' - ' + LTRIM(RTRIM(a.CUSTOMER_NAME)) AS Origin_Customer_TX
, LTRIM(RTRIM(a.ORIGIN_CITY)) + ' - ' + LTRIM(RTRIM(a.DESTINATION_CITY)) AS Origin_Destination_TX
, LTRIM(RTRIM(a.CUSTOMER_NAME)) AS Customer_Name
, LTRIM(RTRIM(a.CUSTOMER_NAME)) + ', ' + LTRIM(RTRIM(a.CUSTOMER_NO)) AS Customer_TX
, CASE
WHEN LTRIM(RTRIM(a.CUSTOMER_TYPE)) = 'C' THEN 'Customer'
WHEN LTRIM(RTRIM(a.CUSTOMER_TYPE)) = 'I' THEN 'Internal'
WHEN LTRIM(RTRIM(a.CUSTOMER_TYPE)) = 'S' THEN 'Shop'
WHEN LTRIM(RTRIM(a.CUSTOMER_TYPE)) = '' THEN 'zUnkown'
ELSE LTRIM(RTRIM(a.CUSTOMER_TYPE))
END AS Customer_Type
, CASE
WHEN a.CARE_OF_NAME = '' THEN 'zUnknown'
ELSE a.CARE_OF_NAME
END AS Care_of_Name
, LTRIM(RTRIM(a.ORIGIN_CITY )) AS Origin_City
, LTRIM(RTRIM(a.ORIGIN_STATE )) AS Origin_State
, LTRIM(RTRIM(a.DESTINATION_CITY )) AS Destination_City
, LTRIM(RTRIM(a.DESTINATION_STATE )) AS Destination_State
, LTRIM(RTRIM(b.BusinessGroup_TX )) AS BusinessGroup_TX
, b.Fleet_TX AS Fleet_TX
, c.Leg_TX AS Leg_TX
FROM zt_Arrival_Data a
INNER JOIN zt_Tpl_Tuple_Stats_2 b
ON LTRIM(RTRIM(a.ORIGIN_CITY)) + ', ' + LTRIM(RTRIM(a.ORIGIN_STATE)) = b.ORIGIN_TX
AND LTRIM(RTRIM(a.DESTINATION_CITY)) + ', ' + LTRIM(RTRIM(a.DESTINATION_STATE)) = b.DESTINATION_TX
AND a.CUSTOMER_NO = b.CUSTOMER_CD
AND a.BUSINESS_GROUP = b.BusinessGroup_TX
AND a.[FLEET_ID (GEN PLANT)] = b.Fleet_TX
JOIN zt_LegMap c ON c.Leg_CD = b.Leg_CD
It is far better to trim the data on data entry where it only has to happen once than to do this sort of thing against a large table in a select.
It is an especially bad design that you you have to concatenate in order to join. You lose the ability to use indexes when you do these things. In SQL Server I would create a calculated persisted column that Ci could join on instead, not sure if mysql has such things. But you should investigate doing this.
From my experience I've learned that when you need to format fields in order to join the tables you should format the columns of the table which has LESS rows to match the one with MORE rows, which must be compared unaltered.
Some idea to start with:
FROM zt_Arrival_Data a
INNER JOIN zt_Tpl_Tuple_Stats_2 b
ON a.ORIGIN_CITY = <format the b table columns to match a.ORIGIN_CITY>
AND a.DESTINATION_STATE = <format the b table columns to match a.DESTINATION_STATE>
AND a.DESTINATION_CITY = <format the b table columns to match a.DESTINATION_CITY>
AND a.ORIGIN_STATE = <format the b table columns to match a.ORIGIN_STATE>
AND a.CUSTOMER_NO = b.CUSTOMER_CD
AND a.BUSINESS_GROUP = b.BusinessGroup_TX
AND a.[FLEET_ID (GEN PLANT)] = b.Fleet_TX
I've kludged my way through by doing this, but I feel there's probably a cleaner/faster/better solution, and am always keen to learn:
UPDATE myTable SET fAvg =
(COALESCE(f1,0) + COALESCE(f2,0) + COALESCE(f3,0) + COALESCE(f4,0))
/
(
CASE WHEN f1 IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN f2 IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN f3 IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN f4 IS NOT NULL THEN 1 ELSE 0 END
)
So, how can that be improved?
You can simplify the count by taking advantage of the fact the MySQL treats booleans as integers in a numeric context:
UPDATE myTable
SET fAvg = (COALESCE(f1,0) + COALESCE(f2,0) + COALESCE(f3,0) + COALESCE(f4,0)) /
((f1 IS NOT NULL) + (f2 IS NOT NULL) + (f3 IS NOT NULL) + (F4 IS NOT NULL));
I have strings like... "3/4" and "5/9" and some like... "1/2 km" and "3/4 degree" stored in mysql columns.
I would like to convert them into numbers.
In first case, 3/4 ==> .75.
In more complicated second case, strip off units like "km" and "degree" so
"1/2 km" ==> 0.5.
I think this isn't something you should do in a query, but rather calculate it when it is stored and save the calculated value in the table next to its text value.
But if you want to, you can use some string functions to slice up the value and the do the math yourself:
select
x.Multiplier / x.Divider as Result
from
(select
cast( substr( t.String,
1,
locate('/', t.String) - 1)
as decimal)
as Multiplier,
cast( substr( t.String,
locate('/', t.String) + 1,
locate( ' ',
concat(t.String, ' ')))
as decimal)
as Divider
from
YourTable t) x
Note however, that this may cause trouble if the data is 'invalid'. If it says '0/0 km', it may fail, if it contains 'no data here' it may fail as well.
The above didn't quite work for me so came up with this which does. The query works for '10', '3/4' and '10 3/4'. Obviously you should replace the constructed rows at the bottom with your string or a selected value from a table:
SELECT
IF (
LOCATE(' ',fraction) > 0 OR LOCATE('/',fraction) = 0,
SUBSTRING_INDEX(SUBSTRING_INDEX(fraction,'/','1'),' ','1')
,0
) AS `integer`,
IF (
LOCATE('/',fraction) > 0,
SUBSTRING_INDEX(SUBSTRING_INDEX(fraction,'/','1'),' ','-1'),
0
) AS numerator,
SUBSTRING_INDEX(fraction,'/','-1') AS denominator,
(SELECT `integer`) + ((SELECT numerator) / (SELECT denominator)) AS `decimal`
FROM (
SELECT '10 3/4' AS fraction
UNION SELECT '10'
UNION SELECT '3/4'
) t;
With PHP you could do:
// assuming $vals has the values from the database
$converted = array();
foreach ($vals as $key => $val) {
preg_match("/^(\\d+)\\/(\\d+)/", $val, $matches)
if (count($matches) > 2) {
$numerator = (int) $matches[1];
$denominator = (int) $matches[2];
$converted[$key] = (float) $numerator / $denominator;
}
}
I have a table, MapLocation, which has a column and two relationships with tables that have a field that really need to be displayed as a single concatenated value. I was thinking this was a perfect case for a computed column, but not sure how to go about it.
MapLocation MaoNo Section
_____________________ _____________________ _____________________
MapNoId MapNoId SectionId
SectionId MapNumber (int) Section (int)
Identifier (nvarchar)
LocationName (nvarchar)
LocationName = "MapNUmber - SectionNumber - Identifier"
ex: 20 - 03 - SW4
How would I write that? I haven't done much with computed columns or concatenating in SQL.
Edit:
I need an actual computed column that is automatically updated, im looking for the formula. Or is this more of a function/trigger? Its possible, I certainly barely know what I'm doing. The idea is that I dont want to have to do two more server calls and concatenate these values client side.
You would use something like this to get the value:
select cast(n.MapNumber as nvarchar(10)) + ' - ' -- cast the MapNumber
+ cast(s.SectionId as nvarchar(10)) + ' - ' -- cast the SectionId
+ l.Identifier
from MapLocation l
left join MaoNo n
on l.MapNoId = n.MapNoId
left join Section s
on l.SectionId = s.SectionId
Then if you need to perform an UPDATE:
update l
set l.LocationName = (cast(n.MapNumber as nvarchar(10)) + ' - '
+ cast(s.SectionId as nvarchar(10)) + ' - '
+ l.Identifier)
from MapLocation l
left join MaoNo n
on l.MapNoId = n.MapNoId
left join Section s
on l.SectionId = s.SectionId
Edit #1 - you can use a TRIGGER:
CREATE TRIGGER trig_LocationName
ON MapLocation
AFTER INSERT
AS
Begin
update MapLocation
set LocationName = (cast(n.MapNumber as nvarchar(10)) + ' - '
+ cast(s.SectionId as nvarchar(10)) + ' - '
+ i.Identifier)
from Inserted i
left join MaoNo n
on i.MapNoId = n.MapNoId
left join Section s
on i.SectionId = s.SectionId
where MapLocation.MapNoId = i.MapNoId -- fields here to make sure you update the correct record
End
I am having some real difficulty converting the below MSSQL statement to produce the same results in MYSQL.
The MSSQL query is as follows:
SELECT Radius_AdslUser.Password, Radius_AdslIp.IpAdd, Route =
CASE WHEN Radius_AdslIP.Route IS NULL
THEN NULL
WHEN Radius_AdslIp.TotalIp = 8
THEN Radius_AdslIP.Ipadd + '/29 ' + SUBSTRING(Radius_AdslIp.Route, 10, Charindex(' ', Radius_AdslIp.Route) - 10) + ' 1'
WHEN Radius_AdslIp.TotalIp = 4
THEN Radius_AdslIP.Ipadd + '/30 ' + SUBSTRING(Radius_AdslIp.Route, 10, Charindex(' ', Radius_AdslIp.Route) - 10) + ' 1'
WHEN Radius_AdslIp.TotalIp = 16
THEN Radius_AdslIP.Ipadd + '/28 ' + SUBSTRING(Radius_AdslIp.Route, 10, Charindex(' ', Radius_AdslIp.Route) - 10) + ' 1'
END, AVPair =
CASE WHEN Radius_AdslUser.bandcap IS NULL
THEN NULL
ELSE 'throttle=' + Radius_AdslUser.bandcap
END, Radius_AdslIp.Subnet
FROM Radius_AdslUser,Radius_AdslIp
WHERE Username = 'username#test.tld'
AND Suspended = 0
AND Radius_AdslUser.IpAddId = Radius_AdslIp.IpAddId
Which produces the following result:
Password IpAdd Route AVPair Subnet
kmbjdatr 10.0.0.1 10.0.0.1/29 10.0.0.0 1 NULL 255.255.255.255
The format of the tables are as follows:
Radius_AdslIp
IpAddId IpAdd Subnet TotalIp UsableIp Route TypeId
944 10.0.0.1 255.255.255.255 8 6 ip:route=10.0.0.0 255.255.255.248 4
Radius_AdslUser
Username Password IpAddId
username#test.tld kmbjdatr 944
I have substituted CHARINDEX for LOCATE and have used CONCAT but am not having much luck.
For example:
WHEN Radius_AdslIp.TotalIp = 8 THEN Radius_AdslIp.Ipadd = CONCAT(Radius_AdslIp.Ipadd, '/29 ')
The table formats in MYSQL are identical.
Any help would be greatly appreciated.
Seems like you are not applying the CONCAT function correctly. Basically, a SQL Server expression like this:
column_name + 'constant string' + function_call() + #variable
would look like this in MySQL:
CONCAT(column_name, 'constant string', function_call(), #variable)
The particular wrong part in your excerpt seems to be Radius_AdslIp.Ipadd = (the one before CONCAT). Not sure what you meant by that, but in any event it's just not needed there.