Multiple IF statements on MYSQL - mysql

I'm trying to Display somes values in my database result, I am using this code but I can not succeed:
SELECT
item_code,
IF(category_code = 'HERR1', 'NO', 1) OR (category_code = 'COLN5', 'NO', 2) AS category_code,
item_name,
item_quantity
FROM qa_items
EDIT :
I Want to display for example:
If category_code = 'HERR1'
Display = 1
else if category_code = 'COLN5'
Display = 2
End If
If anyone has any idea, would greatly appreciate it

I'd rather use CASE :
SELECT item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END as category_code, item_name, item_quantity
FROM qa_items
But IF will also work : IF(category_code='HERR1',1, IF(category_code='COLN5',2,'NO'))

You need to nest the if statements
SELECT item_code, IF(category_code = 'HERR1', 'NO', IF(category_code = 'COLN5', 1, 2)) AS category_code, item_name, item_quantity FROM qa_items
Then the first if will fail and the nested if will evaluate

Is this what you were after?
SELECT
item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END AS category_code,
item_name,
item_quantity
FROM qa_items

Try the following
SELECT item_code, CASE category_code WHEN 'HERR1' THEN 1 WHEN 'COLN5' THEN 0 ELSE 'NONE' END AS category_code, item_name, item_quantity FROM qa_items

You can try this.
Use IF in select query and update the table you want ;)
create table student(marks int,grade char);
insert into student values(200,null),(120,null),
(130,null);
UPDATE student a
INNER JOIN (select s.marks, IF(s.marks>=200,'A',IF(s.marks>=130,'B','P')) AS Grade from student s) b on a.marks= b.marks
SET a.Grade = b.Grade;

SELECT MOBILE,
CASE (SUBSTRING(mobile, LENGTH(MOBILE), 1)) WHEN ','
THEN SUBSTRING(mobile, 1, LENGTH(MOBILE) - 1)
ELSE SUBSTRING(mobile, 1, LENGTH(MOBILE))
END AS newmobile
FROM (SELECT CONCAT(IFNULL(`mob1`, ''), IF(`mob1` IS NULL, '', ','),
IFNULL(`mob2`, ''), IF(`mob2` IS NULL, '', ','),
IFNULL(`mob3`, ''), IF(`mob3` IS NULL, '', ','),
IFNULL(`mob4`, ''), IF(`mob4` IS NULL, '', ','),
IFNULL(`mob5`, ''), IF(`mob5` IS NULL, '', ','),
IFNULL(`mob6`, ''))
AS mobile
FROM `temp_consignordata`) AS T

SELECT item_code,
-- First if
IF(category_code = 'HERR1', 1,
-- second else IF
IF(category_code = 'COLN5', 2,
-- last else
'NO')
AS category_code,
item_name,
item_quantity
FROM qa_items;
Explanation
first if evalutes for value 'HERR1' and if found assigns 1
Second (else ) IF evalues for Value 'COLN5' and if found assigns 2
Last (else) default case assigns 'NO'
to category_code

Related

MySQL Multiple Case When Exists Statement

I have two tables. Let's call it: SEATS and SEAT_ALLOCATION_RULE table.
Below are the table schema:
CREATE TABLE IF NOT EXISTS `SEATS` (
`SeatID` int(11) NOT NULL AUTO_INCREMENT,
`SeatName` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`SeatID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `SEATS` (`SeatID`, `SeatName`) VALUES
(1, 'Super VIP'),
(2, 'VIP'),
(3, 'Business'),
(4, 'Economy'),
(5, 'Standing');
CREATE TABLE IF NOT EXISTS `SEAT_ALLOCATION_RULE` (
`SeatID` int(11) NOT NULL DEFAULT '0',
`Origin` varchar(50) NOT NULL DEFAULT '0',
`Destination` varchar(50) NOT NULL DEFAULT '',
`Passenger_Type` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`SeatID`,`Origin`,`Destination`,`Passenger_Type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `SEAT_ALLOCATION_RULE` (`SeatID`, `Origin`, `Destination, `Passenger_Type`) VALUES
(1, 'Malaysia','',''),
(2, 'Malaysia','Singapore',''),
(3, 'Malaysia','Singapore','Senior_Citizen'),
(4, 'Bangkok','Japan','Student'),
(5, 'Cambodia','China','Senior_Citizen');
SEAT_ALLOCATION_RULE table determines which seat should a passenger be assigned to based on the following order in priority:
1. Origin, destination, and passenger_type match
2. Origin and destination match
3. Origin match
It means that if all the fields (origin, destination, and passenger_type) match, it should take higher priority than if it is just two fields match and so on. If a column is empty, it is considered as unspecified and hence has lower priority. So, for example:
If the Origin is Malaysia, Destination is Singapore, and Passenger_Type is Senior_Citizen, it should return seatID 3
If the Origin is Malaysia, Destination is Singapore, and Passenger_Type is Student, it should return seatID 2 (since it only match Origin and Destination)
If the Origin is Malaysia, Destination is US, and Passenger_Type is Student, it should return seatID 1 (since it only match Origin).
Now, based on the rules above, if the origin is Malaysia, destination is Singapore, and Passenger_Type is student, the query to return seatID is as follow:
SELECT s.SeatID, s.SeatName
FROM SEATS s
WHERE
CASE WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = 'Singapore'
AND r.Passenger_Type='Student') Then 1
WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = 'Singapore'
AND r.Passenger_Type='') Then 1
WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = ''
AND r.Passenger_Type='') Then 1 END
However, the query above does not work as it will return seatID 1 and 2, but the expected output is only seatID 2 (since origin and destination matches and it takes higher precedence). Can someone help to correct my SQL query?
This should do the trick:
select seatid
from seat_allocation_rule sar
order by ((sar.origin = :origin) << 2) + ((sar.destination = :destination) << 1) + (sar.passenger_type = :passenger_type) desc,
((sar.origin <> '') << 2) + ((sar.destination <> '') << 1) + (sar.passenger_type <> '') asc
limit 1
To understand how:
create table testcase (
origin varchar(255),
destination varchar(255),
passenger_type varchar(255),
expected_seat int(11)
);
insert into testcase values ('Malaysia','Singapore','Senior_Citizen',3),
('Malaysia','Singapore','Student',2),
('Malaysia','US','Student',1);
select * from (
select tc.*,
sar.seatid,
case when sar.seatid = tc.expected_seat then 'Y' else '-' end as pass,
((sar.origin = tc.origin) << 2)
+ ((sar.destination = tc.destination) << 1)
+ ((sar.passenger_type = tc.passenger_type) << 0) as score,
((sar.origin <> '') << 2)
+ ((sar.destination <> '') << 1)
+ ((sar.passenger_type <> '') << 0) as priority
from seat_allocation_rule sar
cross join testcase tc
) x order by expected_seat desc, score desc, priority asc;
This fixes the existing SQL:
SELECT DISTINCT s.SeatID, s.SeatName
FROM SEATS s
LEFT JOIN SEAT_ALLOCATION_RULE r ON r.SeatID = s.SeatID
AND r.Origin = 'Malaysia'
AND (
(r.Destination = 'Singapore' AND r.Passenger_Type IN ('Student', ''))
OR
(r.Destination = '' AND r.Passenger_Type = '')
)
WHERE r.SeatID IS NOT NULL
But it's only a partial solution, and it's hand-coding logic you really want to apply based solely on the data.
A complete solution will use hypothetical inputs for your passenger's ticket info to produce all eligible seats. This is a great use of lateral joins/apply, which are sadly lacking in MySql (all of their major competitors have had these for at least two release cycles, along with other gems that are absent from the current MySql release like windowing functions, ctes, full joins... I could go on). Here's how I'd do it in Sql Server:
SELECT p.PassengerID, s.SeatID, s.SeatName
FROM Passenger p
CROSS APPLY (
SELECT TOP 1 r.SeatID
FROM SEAT_ALLOCATION_RULE r
WHERE COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
ORDER BY
CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END DESC
) r
INNER JOIN SEATS s ON s.SeatID = r.SeatID
WHERE p.PassengerID = /* passenger criteria here */
I know the Sql Server solution isn't much immediate help to you, but perhaps it will suggest a better MySql solution.
Without APPLY, the only way I know to do this is to first compute the MAX() match count for your passengers (how many parts of the rules match):
SELECT p.PassengerID,
MAX(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END) AS MatchCount
FROM Passenger p
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
GROUP BY p.PassengerID
And then use that to filter down to results that have the same number of matches:
SELECT p
FROM Passenger p
INNER JOIN ( /* matchecounts */
SELECT p.PassengerID,
MAX(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END) AS MatchCount
FROM Passenger p
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
GROUP BY p.PassengerID
) m ON m.PassengerID = p.PassengerID
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
INNER JOIN SEATS s ON s.SeatID = r.SeatID
WHERE m.MatchCount =
(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END)
AND p.PassengerID = /* Passenger criteria here */
Which repeats a lot of code as well as effort in the DB, and is not very efficient. You can repeat the passenger criteria in the nested query, but that would only help a little. This option might also return multiple records for a passenger if they match two rules equally, though you can solve this easily enough with a GROUP BY expression.
In either case, note you can improve performance and simplify code by using actual NULL values instead of empty strings for missing parts of the SEAT_ALLOCATION_RULE table.

MySQL - Rows to Columns and keeps NULL

We have the following table (TEST2) in the MySQL database (MySQL 5.6):
TEAM_ID,MEMBER_ID,TYPE,SCORE
1,2,A,150
1,3,B,200
1,1,B,50
1,1,A,100
1,2,B,NULL
We try to transform/pivot the above table based on the TYPE column:
If the TYPE column has value == A, move the value in the SCORE column into a new column called A_SCORE. If the value in the SCORE column is NULL, it should show NULL in the new A_SCORE column.
If the TYPE column has value == B, move the value in the SCORE column into a new column called B_SCORE. If the value in the SCORE column is NULL, it should show NULL in the new B_SCORE column.
The following table is the one we are looking for (the wanted table):
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,100,50,50
1,2,150,NULL,NULL
1,3,0,200,-200
We tried the following query
SELECT TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,SUM(A_SCORE-B_SCORE) AS ACTUAL_MINUS_B_SCORE FROM
(SELECT TEAM_ID,MEMBER_ID,
CASE
WHEN SCORE IS NULL
THEN NULL
ELSE SUM(if(TYPE = 'A', SCORE,0) )
END A_SCORE,
CASE
WHEN SCORE IS NULL
THEN NULL
ELSE SUM(if(TYPE = 'B', SCORE,0) )
END B_SCORE
FROM TEST2
GROUP BY TEAM_ID,MEMBER_ID,SCORE) AS A
GROUP BY TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE);
It returns something we don’t want:
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,0,50,-50
1,1,100,0,100
1,2,0,0,0
1,2,150,0,150
1,3,0,200,-200
If we tried the following, it generates a table close to what we want, but it doesn’t return any NULL value.
SELECT TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,SUM(A_SCORE-B_SCORE) AS A_SCORE _MINUS_B_SCORE FROM
(SELECT TEAM_ID,MEMBER_ID,
SUM(if(TYPE = 'A', SCORE,0) ) AS A_SCORE,
SUM(if(TYPE = 'B', SCORE,0) )AS B_SCORE
FROM TEST2
GROUP BY TEAM_ID,MEMBER_ID) AS A
GROUP BY TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE;
The result of the above query:
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,100,50,50
1,2,150,0,0
1,3,0,200,-200
Could any guru enlighten how to generate the wanted table in this case using MySQL? The SQL fiddle is here for your convenience.
http://sqlfiddle.com/#!9/cfe7a1/1
Thanks!
Try this;)
SELECT TEAM_ID, MEMBER_ID, A_SCORE, B_SCORE, A_SCORE - B_SCORE AS A_SCORE_MINUS_B_SCORE
FROM (
SELECT
TEAM_ID, MEMBER_ID,
CASE
WHEN A_SCORE IS NULL AND NOT EXISTS (
SELECT 1 FROM TEST2
WHERE TEAM_ID = T1.TEAM_ID
AND MEMBER_ID = T1.MEMBER_ID
AND TYPE = 'A'
) THEN 0 ELSE A_SCORE END AS A_SCORE,
CASE
WHEN B_SCORE IS NULL AND NOT EXISTS (
SELECT 1 FROM TEST2
WHERE TEAM_ID = T1.TEAM_ID
AND MEMBER_ID = T1.MEMBER_ID
AND TYPE = 'A'
) THEN 0 ELSE B_SCORE END AS B_SCORE
FROM (
SELECT
TEAM_ID, MEMBER_ID,
MAX(CASE WHEN TYPE = 'A' THEN SCORE END) AS A_SCORE,
MAX(CASE WHEN TYPE = 'B' THEN SCORE END) AS B_SCORE
FROM TEST2
GROUP BY TEAM_ID, MEMBER_ID
) T1
)T
SQLFiddle demo here
I don't quite understand the calculation criteria, but something like this should work...
SELECT team_id
, member_id
, COALESCE(MAX(CASE WHEN type = 'A' THEN score END),0) a_score
, COALESCE(MAX(CASE WHEN type = 'B' THEN score END),0) b_score
, COALESCE(MAX(CASE WHEN type = 'A' THEN score END),0)
- COALESCE(MAX(CASE WHEN type = 'B' THEN score END),0) diff
FROM test2
GROUP
BY team_id
, member_id;

Pivot Table Omitting Rows that Have Null values

I am solving a problem very similar to this only in my case, I am not summing any values.
I have been able to write a select that works using solution from this page
SELECT
id,
GROUP_CONCAT(if(colID = 1, value, NULL)) AS 'First Name',
GROUP_CONCAT(if(colID = 2, value, NULL)) AS 'Last Name',
GROUP_CONCAT(if(colID = 3, value, NULL)) AS 'Job Title'
FROM tbl
GROUP BY id;
However, I want to omit rows that have the value to be null
I assume you want to drop the result row if any of the source rows has value IS NULL.
You should be able to achieve that with bit_and() in the HAVING clause:
SELECT id
, max(CASE WHEN colID = 1 THEN value END) AS fn
, max(CASE WHEN colID = 2 THEN value END) AS ln
, max(CASE WHEN colID = 3 THEN value END) AS jt
FROM tbl
GROUP BY id
HAVING bit_and(value IS NOT NULL);
Alternative:
...
HAVING count(*) = count(value);
I didn't spell out ELSE NULL in the CASE statements because (per documentation):
If there was no matching result value, the result after ELSE is returned, or NULL if there is no ELSE part.
Just add this constraint to the where statement of your query, like this:
SELECT
id,
GROUP_CONCAT(if(colID = 1, value, NULL)) AS 'First Name',
GROUP_CONCAT(if(colID = 2, value, NULL)) AS 'Last Name',
GROUP_CONCAT(if(colID = 3, value, NULL)) AS 'Job Title'
FROM tbl
WHERE value IS NOT NULL
GROUP BY id;
EDIT
After some tests I could make a solution to work, but it seems interesting why value is not null won't work.
Solution link: http://sqlfiddle.com/#!2/b7a445/3
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
where not exists (select * from tbl b where tbl.id=b.id and value is null)
group by id

return a SUM zero(0) instead of null

SELECT SUM( IF( userId = '123456', amount, 0 ) ) AS 'amount'
FROM `amountInfo`
userId 123456 is not present in table amountInfo, in that case it is returning null i want 0(numerical)
Use IFNULL:
SELECT IFNULL(SUM(IF(userId = '123456', amount, 0)), 0) AS amount
You can use coalesce for this
UPDATE : I was missing the argument to set 0 instead of NULL, pointed by #Barmar
coalesce( SUM( IF( userId = '123456', amount, 0 ) ),0 )
Select COALESCE(sum(amount),0) as amount from amountInfo where userId='123456'
You can read on http://www.w3schools.com/sql/sql_isnull.asp

Compare Months list with date field of table

I am using sql server 2008
I have table in my database is like this:
And I want output like this:
As it is shown in my table I have DateField which has smalldatetime datatype and along with fruits and vegi fields. I want output like which shows data month-wise.. month comparison should be performed based on DateField of my table.
You can use something like:
select [Month] = month(DateField)
, [MonthName] = left(datename(mm, DateField), 3)
, TotalAmountApple = sum(case when fruits = 'Apple' then 1 else 0 end)
, TotalAmountOnion = sum(case when vegi = 'Onion' then 1 else 0 end)
from produce
group by month(DateField)
, left(datename(mm, DateField), 3)
order by [Month]
Full test details (no SQL Fiddle as it's experiencing issues):
create table produce
(
id int
, fruits varchar(10)
, vegi varchar(10)
, DateField smalldatetime
)
insert into produce
select 1, 'Apple', 'Chilly', '01-jan-2013'
insert into produce
select 1, 'Mango', 'Onion', '15-jan-2013'
insert into produce
select 1, 'Mango', 'Chilly', '20-jan-2013'
insert into produce
select 1, 'Apple', 'Chilly', '01-Feb-2013'
insert into produce
select 1, 'Mango', 'Onion', '15-Feb-2013'
insert into produce
select 1, 'Apple', 'Onion', '20-Feb-2013'
select [Month] = month(DateField)
, [MonthName] = left(datename(mm, DateField), 3)
, TotalAmountApple = sum(case when fruits = 'Apple' then 1 else 0 end)
, TotalAmountOnion = sum(case when vegi = 'Onion' then 1 else 0 end)
from produce
group by month(DateField)
, left(datename(mm, DateField), 3)
order by [Month]