mysql IF Else Statement - mysql

Not sure how far a sql query can go with if/else statements.
I have a simple SELECT statement:
SELECT amount, transtype FROM
transactions
The transtype column is going to be a number.
For example, 1 = sale, 2 = refund, 3 = error, 4 = canceled, 5 = something else.... and so on.
So, nothing complicated. But the list tends to grow for reporting reasons. Which is fine.
For a specific query I'm working on, is there a way to extract that column as one of 2 or three specified numbers or text?
For example, some transtype numbers are a 'loss', while others are a 'gain', and maybe others are 'neutral'.
I'd like to extract that column with only those 3, without using php inside the html table I'm throwing the rows into.
If my explanation is not clear, my apologies. It was hard to spit out.

Use the MySQL CASE() function for a fixed number of arguments. If the list is getting big, you should use a second table and join them.
Example:
SELECT CASE WHEN 1>0 THEN 'true' ELSE 'false' END;

Try joining against another table that contains the transaction types. Something like:
TRANSACTION_TYPES
transtype | number
label | varchar(32)
Then modify your query to include the join:
select t.amount, t.transtype, l.label
from transactions.t
join transaction_types l on t.transtype = l.transtype;

The ELT function should do the trick:
SELECT ELT(`transtype`, 'loss', 'loss', 'gain', 'neutral', …) FROM …
Not very elegant though, and logically I would do this in the view logic, not the database logic.

You should probably use ENUM type for that column. It's limited to 64 values, however if you would need more then you should create a new table and JOIN it in query.

Related

How to define a constant value in MySQL query

I need to run some scripts for MySQL databases. And I need to adjust the value in queries each time I run them. However, I need to specify the same value in two different places. I don't want to accidentally leave one value unchanged. Is there a way to specify the value as a constant in the script? I only have read privilege in the databases. I couldn't find the information through searching. Thanks.
You could probably do what you want with MySQL #variables, or via a simple select statement as your first table and join with no ON clause (thus a cross-join), but a Cartesian result with only 1 record will never create duplicates. Then you can use that column consistently throughout. For example...
select
from
( select #someNumber := 123,
#someDate := '2017-07-23' ) sqlVars,
OtherTable OT
where
OT.SomeNumberField = #someNumber
OR OT.OtherDateField = #someDate
but you can probably do similar as just a column such as
select
from
( select 123 as someNumber,
'2017-07-23' as someDate ) sqlVars,
OtherTable OT
where
OT.SomeNumberField = sqlVars.someNumber
OR OT.OtherDateField = sqlVars.someDate
Of course, standard join, left-join, etc to multiple tables should be able to see the columns either way with the table as the first in the list so it is visible all down-stream.
If you define a variable at the beginning of your query, you can use that throughout.

Union query to combine results of 3 tables

I am relatively new to coding so please have patience.
I am trying to combine data from 3 tables. I have managed to get some data back but it isn't what i need. Please see my example below.
select oid, rrnhs, idnam, idfnam, dte1, ta
as 'access type' from person
left join
(select fk_oid, min(dte), dte1, ta
from
((Select fk_oid,min(accessdate) as dte, accessdate1 as dte1, accesstype as ta
from vascularpdaccess
where isnull(accesstype)=false group by fk_oid)
union
(Select fk_oid, min(hpdate) as dte, hpdate as dte1, HPACCE as ta
from hdtreatment
where isnull(hptype)=false group by fk_oid)) as bla
group by fk_oid) as access
on person.oid=access.fk_oid
where person.rrnhs in (1000010000, 2000020000, 3000030000)
My understanding with a union is that the columns have to be of the same data type but i have two problems. The first is that accesstype and hpacce combine in to a the same column as expected, but i dont want to actually see the hpacce data (dont know if this is even possible).
Secondly, the idea of the query is to pull back a patients 'accesstype' date at the first date of hpdate.
I dont know if this even makes sens to you guys but hoping someone can help..y'all are usually pretty nifty!
Thanks in advance!
Mikey
All queries need to have the same number of columns in the SELECT statement. It looks like you first query has the max number of columns, so you will need to "pad" the other to have the same number of columns. You can use NULL as col to create the column with all null values.
To answer the question (I think) you were asking... for a UNION or UNION ALL set operation, you are correct: the number of columns and the datatypes of the columns returned must match.
But it is possible to return a literal as an expression in the SELECT list. For example, if you don't want to return the value of HPACCE column, you can replace that with a literal or a NULL. (If that column is character datatype (we can't tell from the information provided in the question), you could use (for example) a literal empty string '' AS ta in place of HPACCE AS ta.
SELECT fk_oid
, MIN(HPDATE) AS dte
, hpdate AS dte1
, NULL AS ta
-- -------------------- ^^^^
FROM hdtreatment
Some other notes:
The predicate ISNULL(foo)=FALSE can be more simply expressed as foo IS NOT NULL.
The UNION set operator will remove duplicate rows. If that's not necessary, you could use a UNION ALL set operator.
The subsequent GROUP BY fk_oid operation on the inline view bla is going to collapse rows; but it's indeterminate which row the values from dte1 and ta will be from. (i.e. there is no guarantee those values will be from the row that had the "minimum" value of dte.) Other databases will throw an exception/error with this statement, along the lines of "non-aggregate in SELECT list not in GROUP BY". But this is allowed (without error or warning) by a MySQL specific extension to GROUP BY behavior. (We can get MySQL to behave like other databases and throw an error of we specify a value for sql_mode that includes ONLY_FULL_GROUP_BY (?).)
The predicate on the outer query doesn't get pushed down into the inline view bla. The view bla is going to materialized for every fk_oid, and that could be a performance issue on large sets.
Also, qualifying all column references would make the statement easier to read. And, that will also insulate the statement from throwing an "ambiguous column" error in the future, when a column named (e.g.) ta or dte1 is added to the person table.

CONCAT in WHERE clause of SELECT statement in mysql

M2014 is a text field in the DB table.
This statement works correctly (returns count = 368)
SELECT count(*) FROM arealist WHERE M2014 = 'Yes'
However, I having problems with this statement (returns count = 0) All I have changed
is the concat
SELECT count(*) FROM arealist WHERE concat('M','2014') = 'Yes'
What could be the cause and solution?
You are comparing two strings in the second SELECT statement. The second statement is appending two strings 'M' and '2014' which results in the query comparing 'M2014' to 'Yes' two strings, not the value of the column. Making a statement like this:
SELECT COUNT(*)
FROM AreaList
WHERE M2014 = CONCAT('Y','es')
That statement would return 368 rows. What are you ultimately trying to do with this statement?
You can't generate a dynamic column name for the where clause in MySQL. There are a number of Stack Overflow articles to that effect. Normally, I would arrange my data so that there was some sort of date or timestamp associated with the row, rather than using date specific columns. (I'm assuming M2014 has something to do with the year 2014). When arranged this way you can select what you need based on whatever date requirements you have.
That said, if your data model is fixed, then you're best bet is probably to use another language, C#, python, whatever, to create the column names you need dynamically and then send the entire query to MySQL. Alternatively, you could write a series of SQL statements, one for date column you're interested in.
The following query in google turned up a number of relevant results: https://www.google.com/search?client=opera&q=dynamic+column+names+in+sql&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest&safe=active#channel=suggest&q=dynamic+column+names+in+mysql+where+clause&safe=active
you can do it in php like that
$year = 2014;
SELECT count(*) FROM arealist
WHERE M$year = 'Yes'

Need a SQL Server 2008 case statement to evaluate a table and return two values

I'm a novice SQL programmer and have been banging my head against this all morning, so please bear with me. My situation is this: I have a table of SKUs that need to be sent to our eCommerce website. Each of these SKUs has a 'quantity', an 'active' value, and a 'discontinued' value. This was easy enough to handle when we were dealing with one SKU at a time, but now I have to send kits, which contain one or more SKUs.
For example, if my Kit's ID is 000920_001449_001718_999999 (a combination of four SKUs) I need to collect data for the entire set of SKUs like so:
Here's the logic I need to incorporate:
If any of the SKUs have null or WEBNO as an IsActive value, the entire kit must return WEBNO. Otherwise, return WEBYES.
If any of the SKUs have null or '1' as an IsDiscontinued value, the entire kit must return IsDiscontinued = '1'. Otherwise, return a 0.
My code is a bit of a mess, but here's what I've managed so far:
SELECT
CASE WHEN 'WEBNO' in
(
SELECT IsActive
FROM #SkusToSend as Sending
RIGHT JOIN
(
SELECT * FROM [eCommerce].[dbo].[Split] (
'000920_001449_001718_999999'
,'_')
) as SplitSkus
on Sending.SKU = SplitSkus.items
) THEN 'WEBNO'
ELSE 'WEBYES'
END
My question is this: Is it possible to write a statement that parses through my example table, returning only one row of 'IsActive' and 'IsDiscontinued'? I've tried using GROUP BY and HAVING statements on those fields, but always get multiple rows returned.
The code I have handles the WEBNO value, but not NULL, and doesn't even start to take into consideration the IsDiscontinued field yet. Is there a concise way to parse this together, or a better way to handle this type of problem?
I think a combination of ISNULL and MIN / MAX should do the trick:
SELECT
MIN(ISNULL(sending.IsActive, 'WEBNO')) AS IsActive,
MAX(ISNULL(sending.IsDiscontinuted, 1)) AS IsDiscontinuted
FROM
(
SELECT * FROM [eCommerce].[dbo].[Split] (
'000920_001449_001718_999999'
,'_')
) AS SplitSkus
LEFT JOIN #SkusToSend AS Sending
AS Sending.SKU = SplitSkus.items
I think this would be easier if you had a working example of some sample data in those tables. From guessing it looks like you have a table function splitting a string apart and giving multiple rows. You have some temp table that right joins to that so that is taking the function and essentially returning all rows it gets even if there are nulls in the temp table. This could return multiple rows as if you have a condition where you expect a single entity on a left or right join and there is a null at times you will get multiples. Or if you have a value repeated you will get multiples. You would have to ensure that you get one one result I am believing from your
Case when 'WEBNO' in
(
As while the logic may be correct to return the 'WEBNO' answer, it may be repeating the row result multiple times as the engine may interpret 'this happened' once, twice, three times. You could alleviate this by potentially doing a
'Select Distinct IsActive'
Which will make the expression return only a single result that is distinct for that column return.
Again this would be easier if we could see examples of what data those objects contained but this would be my guess.

MySQL sort by 2 parameters

I have a MySQL table with 2 fields:
pd_code and pd_sort (pd_sort default value=0). For each product it is possible to specify an order index (in pd_sort) -1000, -900 and so on.
So when I print out products in PHP, i would like to sort them out like this.
product1 (pd_sort = -100), product2 (pd_sort = -90) etc, and then the rest products (where pd_sort = 0) sorted by pd_code.
ORDER BY pd_sort,pd_code works only for 2 products.
Any suggestions?
Chris
If I understand right, you should try something like this:
SELECT * FROM table
WHERE pd_sort <> 0
ORDER BY pd_sort
UNION
SELECT * FROM table
WHERE pd_sort = 0
ORDER BY pd_code
A union as jab suggested should be fairly efficient, even if it does result in two queries rather than one.
If you don't want to do the union for whatever reason, another approach is to have the select generate a column by manipulating the pd_code and pd_sort values, and sort on that column. You haven't given us sample data to work with (well, other than a couple of pd_sort values), but in most cases it's possible to manipulate the data such that you end up with a sortable value, usually just by doing concats or numeric expressions. But in the most complex cases, you can fall back on case statements.