MySQL stored procedure: IF in WHERE - mysql

I am trying to select some data in the following manner:
SELECT column
FROM table
WHERE a = a1
AND (b = b1 OR b = b2 OR b = b3);
What I want it to do is if b is not equal to b1, check if b=b2. However, if b=b1, do not check other conditions.
The result of this select statement must be only one entry. However, in the statement I have no, it checks all of the three conditions and sometimes returns multiple rows. Again, I would like it to stop checking if the condition is true.
Any ideas on how could this be implemented? I tried case but it did not work out...
Thank you in advance!
EDIT
Here is an actual query i am trying to run.
INSERT INTO shipment_flights
(airlinename, flt_no, flt_date, destination, phone, depttime, arrivaltime, pcs, weight)
SELECT st.airlinename, flightno, flightdate, destination,
(SELECT phone
FROM carrierlocations
WHERE carriers_carrierid = (select carrierid from carriers where airlinename = st.airlinename)
AND (city = destination OR (city != destination AND
city = (SELECT city FROM airports WHERE iataid =
(SELECT airports_iataid FROM ratelegs
WHERE shipments_shipid = c.shipments_shipid))
))) phone,
depttime, arrivaltime, sum(linepcs), sum(lineweight)
FROM segment_times st
JOIN contents2flights c2f
ON st.flightid = c2f.segments_flights_flightid
AND st.segmentid = c2f.segments_segmentid
JOIN contents c
ON c.lineno = c2f.contents_lineno
AND c.shipments_shipid = c2f.contents_shipments_shipid
WHERE c.shipments_shipid = var_shipid
GROUP BY flightid
ORDER BY flightdate, depttime;
Here is a sample output:
airlinename flt_no flt_date destination phone pcs weight
Everts Air Alaska CH1 2008-02-20 Hughes 9074502351 24 2121
The query inserts bunch of flight data into temporary table. What I am having trouble with is getting a phone number for a location. This part is as follows:
(SELECT phone
FROM carrierlocations
WHERE carriers_carrierid = (select carrierid from carriers where airlinename = st.airlinename)
AND (city = destination OR (city != destination AND
city = (SELECT city FROM airports WHERE iataid =
(SELECT airports_iataid FROM ratelegs
WHERE shipments_shipid = c.shipments_shipid))))) phone
In the query advised by Amit Bhargava, I get the right result only if there is one row in the temporary table. If there are more, it throws an error in the selecting phone part.
"Error Code: 1242. Subquery returns more than 1 row"

By using IF() + IF() + IF(), and testing the sum = 1 prevents a lot of extra and not of the other criteria. If 2 or all 3 are the same, the summation will be greater than 1. If none of them match, the result is 0. This should get exactly what you want.
SELECT column
FROM table
WHERE a = a1
AND if( b = b1, 1, 0 )
+ if( b = b2, 1, 0 )
+ if( b = b3, 1, 0 ) = 1
Or... as assisted by ypercube, and I keep forgetting logical tests return either 1 or 0 for true / false respectively, such as...
SELECT column
FROM table
WHERE a = a1
AND (b = b1) + (b = b2) + (b = b3) = 1

Please try the following. Not the most elegant solution, but it should work.
SELECT column
FROM table
WHERE a = a1
AND (b = b1 OR (b != b1 AND (b = b2 OR (b != b2 AND b = b3))))

Related

How to fetch rows

I'm trying to extract rows from database depending on two columns where only one column is indexed (primary key) and other is not .
Example : table A has columns a(PK), b(date feild), c
Now I'm trying to get rows where b = 29-04-2019 and a = 1 , b = 30-04-2019 and a = 2 . In this case i can only get 4 rows 2 rows for a = 1 (both dates) and two rows for a = 2 (both dates) but I need only two rows totally .
I have tried this Query :
select * from A
where a in (1,2)
and b in ("2019-04-29","2019-04-30")
You need the SELECT using WHERE with OR operator.
Typical example is:
SELECT * FROM table_name
WHERE condition1
OR condition2;
This will select all matching rows where EITHER condition1 is true, OR condition2 is true.
So in your case:
condition1 is a = 1 AND b = "2019-04-29"
condition2 is a = 2 AND b = "2019-04-30"
Putting it together:
SELECT * from A
WHERE (a = 1 AND b = "2019-04-29")
OR (a = 2 AND b = "2019-04-30")
Note that if you data has more than 1 instance of each condition, you will return more than 2 rows in total.

SQL view to get records that don't match the JOIN condition

I have been trying to get my head around this but not yet found a solution. I am dealing with a SQL view whose results are based on certain conditions. My SQL View is as below
ALTER VIEW [dbo].[vAeoiCaseClose]
AS
SELECT CaseReference, s.AccessNumber
FROM dbo.AeoiSdtTemp s
JOIN AeoiCaseManagement c on c.AccessNumber = s.AccessNumber
JOIN AeoiCaptureLog a on a.AccessNumber = c.AccessNumber
WHERE AscertainMethodId IS NOT NULL
AND c.StatusCode = 15 AND a.StatusCode IN (6, 13, 15)
AND DATEDIFF(dd, s.LastModifiedDate, GETDATE()) <= 80 AND DATEDIFF(dd, a.LastModifiedDate, GETDATE()) <= 80
AND (
(AscertainMethodId = 1 AND ExtendedStatusId = 1)
OR (AscertainMethodId = 2 AND ExtendedStatusId = 1)
OR (AscertainMethodId = 2 AND ExtendedStatusId = 4)
OR (AscertainMethodId = 3 AND ExtendedStatusId = 1)
OR (AscertainMethodId = 3 AND ExtendedStatusId = 4)
OR (AscertainMethodId = 4 AND ExtendedStatusId = 1)
OR (AscertainMethodId = 5 AND ExtendedStatusId = 1)
OR (AscertainMethodId = 5 AND ExtendedStatusId = 4)
)
GO
I have to add another condition, so those records are also avaialble as result of the view.
Condition:
In some cases there might be 2 records in dbo.AeoiSdtTemp with same (AccessNumber) but different (AscertainMethodId) , in this case the record won't be available in AeoiCaptureLog table, But i need those as part of the result of the view.
Please suggest. Appreciate help.
Example:
dbo.AeoiSdtTemp
(AccessNumber) - 1111 (AscertainMethodId) - 3 StatusCode = 15
(AccessNumber) - 1111 (AscertainMethodId) - 5 StatusCode = 11
dbo.AeoiCaptureLog
NO RECORD
dbo.AeoiCaseManagement
(AccessNumber) - 1111 (AscertainMethodId) - 3 StatusCode = 15
A quick and dirty starting point without testing: Use a second alias (s1 and s2 instead of s, or s and s1) to join the dbo.AeoiSdtTemp table to itself using the s.AccessNumber, like so:
...
FROM dbo.AeoiSdtTemp s1
...
JOIN dbo.AeoiSdtTemp s2 ON (
s2.AccessNumber = s1.AccessNumber
AND s2. AscertainMethodId != s1. AscertainMethodId
)
...
Also, do a review of LEFT vs RIGHT and INNER vs OUTER joins, which will help you determine the way that the clause above will affect your resulting data.

Update Case When, with multiple conditions

I have this table:
CREATE TABLE IF NOT EXISTS `my_table` (
`A` int(11) NOT NULL,
`B` int(11) NOT NULL,
`C` varchar(50) NOT NULL,
`D` varchar(30) NOT NULL,
PRIMARY KEY (`A`,`B`,`C`)
)
I want to update several entries in just one query. I tried this:
UPDATE my_table
SET D = CASE
WHEN (A = 6 AND B = 1 AND C = 'red') THEN '1#2#3#5#4'
WHEN (A = 8 AND B = 1 AND C = 'green') THEN '5#6#7#8#9'
END
But this query updates all entries in the table. It updates perfectly the value of 'D' of the two entries I want to update, but it also deletes the values of "D" of the other entries, and I want them to stay with their previous values.
If you do not explicitly add an else clause to a case expression, it implicitly acts as though you've added else null to it. So, your update statement is effectively equivalent to:
UPDATE my_table
SET D = CASE
WHEN (A = 6 AND B = 1 AND C = 'red') THEN '1#2#3#5#4'
WHEN (A = 8 AND B = 1 AND C = 'green') THEN '5#6#7#8#9'
ELSE NULL
END
Which explains why you see D being "deleted".
One way around it is to explicitly add an else clause that simply returns D:
UPDATE my_table
SET D = CASE
WHEN (A = 6 AND B = 1 AND C = 'red') THEN '1#2#3#5#4'
WHEN (A = 8 AND B = 1 AND C = 'green') THEN '5#6#7#8#9'
ELSE D
END
Another way, which is a bit "clunkier" in syntax, but may perform slightly better, is to add a where clause so only the relevant rows are updated:
UPDATE my_table
SET D = CASE
WHEN (A = 6 AND B = 1 AND C = 'red') THEN '1#2#3#5#4'
WHEN (A = 8 AND B = 1 AND C = 'green') THEN '5#6#7#8#9'
END
WHERE (A = 6 AND B = 1 AND C = 'red') OR (A = 8 AND B = 1 AND C = 'green')

MySQL merge table, with zero vaules

Table A name is source
ID | date | valueS | commonID
1 26.8.14 Svalue01 11
2 21.8.14 Svalue02 11
3 25.8.14 Svalue03 11
Table B name is destination
ID | date | valueD | commonID
1 26.8.14 Dvalue01 11
2 21.8.14 Dvalue03 11
3 24.8.14 Dvalue03 11
So currently im using
SELECT a.*, b.* FROM (SELECT * FROM Source WHERE commonID = '11')a JOIN destination b ON a.commonID = b.commonID
But this dont get me the wished result.
i want something sorted by date, and if there is no record for both on the date, one is zero.
example how it should look
ID | date | valueD | commonID | ID | date | valueS | commonID
1 26.8.14 Dvalue01 11 1 26.8.14 Svalue01 11
3 25.8.14 Svalue03 11
3 24.8.14 Dvalue03 11
2 21.8.14 Dvalue03 11 2 21.8.14 Svalue02 11
Is and how would this be possible?
Additional Info:
-Using Mysql 5.5.37 (MariaDB)
-ID is primary on both
-date fields are "timestamp"
-value fields are INT
-ID fields are INT
-Engine is InnoDB
I hope i provided enough information and tried to make a good explained question
thank you for your help
you want to join on the date as that is the determining column so something like this
SELECT
COALESCE(s.id, "") as s_id,
COALESCE(s.date, "") as s_date,
COALESCE(s.valueS, "") as 'valueS',
COALESCE(s.commonID, "") as s_commonID,
COALESCE(d.id, "") as d_id,
COALESCE(d.date, "") as d_date,
COALESCE(d.valueD, "") as 'valueD',
COALESCE(d.commonID, "") as d_commonID
FROM source s
LEFT JOIN destination d on d.date = s.date
AND d.commonID = s.commonID
WHERE d.commonID = 11
UNION
SELECT
COALESCE(s1.id, "") as s_id,
COALESCE(s1.date, "") as s_date,
COALESCE(s1.valueS, "") as 'valueS',
COALESCE(s1.commonID, "") as s_commonID,
COALESCE(d1.id, "") as d_id,
COALESCE(d1.date, "") as d_date,
COALESCE(d1.valueD, "") as 'valueD',
COALESCE(d1.commonID, "") as d_commonID
FROM source s1
RIGHT JOIN destination d1 on d1.date = s1.date
AND d1.commonID = s1.commonID
WHERE d1.commonID = 11
ORDER BY s_date DESC, d_date DESC
DEMO
You need a Full outer Join
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s LEFT JOIN destination d ON (s.id = d.id)
UNION
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s RIGHT JOIN destination d ON (s.id = d.id);
I would go with a different solution for this problem. This starts by generating a cross product of all the common ids and dates that you want, and then using left join to bring in the other rows.
You only want one value for commonid, so this is slight overkill for your problem:
select s.*, dest.*
from (select 11 as commonid) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;
But it is readily extendible. If you wanted two common ids, you could use:
select s.*, dest.*
from (select 11 as commonid union all select 12) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;

How to select MySQL rows that have the same content but in different columns?

I have a MySQL table with three columns, X, Y and Z
| X | Y | Z |
-------------------------------------
| john | patrick | active |
| john | miles | inactive |
| patrick | john | active |
I'd like to select the row data, given 'john' as the input, corresponding to the pair john-patrick - that is, john and patrick are both in either X or Y (like select * rows from TABLE where X = john or Y = john and where ...)
Is it possible? If so, how?
If I understand you correctly, the values need to be in X and Y
(excluding the tuple ('john', 'miles', 'inactive') in the sample).
Then you will need subquerys for this:
select X,Y,Z from TABLE where
(X = 'john' or Y = 'john') and
X in (select Y from TABLE) and
Y in (select X from TABLE);
EDIT: The first query was wrong, thanks to ypercube (see comments).
Corrected version:
select X,Y,Z from TABLE where
(X = 'john' or Y = 'john') and
X in (select Y from TABLE as subTable WHERE
TABLE.X = subTable.Y and TABLE.Y = subTable.X) and
Y in (select X from TABLE as subTable WHERE
TABLE.X = subTable.Y and TABLE.Y = subTable.X);
select a, b, c from test where b in (select distinct a from test) or a in (select distinct a from test)
john patrick active
john miles inactive
patrick john active
There's plenty of ways to solve this problem. This is just one:
SELECT *
FROM users
WHERE
(X = 'john' AND Y = 'patrick')
OR
(Y = 'john' AND X = 'patrick');
SELECT
X, Y, Z
FROM
TABLE AS a
JOIN
TABLE AS b
ON b.X = a.Y
AND b.Y = a.X
WHERE
(a.X = 'john' OR b.X = 'john')
Here's your requirements expressed as SQL:
select a.X, a.Y, a.Z
from mytable a
join mytable b on a.X = b.Y and a.Y = b.X
where a.X = 'john';
This is very similar to ypercube's answer, but differs in one small but very important way: there is no need for or b.Y = 'john' in the where clause, because this is logically covered by the on clause of the join (the join says a.X = b.Y, so if a.X = 'john' we know already that b.Y = 'john').
This difference might seem small, but is large in terms of performance: Databases will not use indexes when OR is used to match indexed columns, ie:
where col = 'x' or col = 'y' -- no: will not use index on col
where col = 'x' -- yes: will use index on col
My query will use an index on column X if one exists). If one doesn't exist, create one.
Incidentally, to get the database to use an index in an OR on a given column, use IN instead. These next two lines are logically identical, but perform very differently
where col = 'x' or col = 'y' -- no: will not use index on col
where col in ('x', 'y') -- yes: will use index on col
You don't need a join for this.
select X,Y,Z from users where X like '%john%' or Y like '%john%';