combining boolean results from two columns SQL - sql

I have the following example data from two Boolean columns:
ID Male Female
1 1 0
2 0 1
3 0 1
4 1 0
5 0 1
I would like to combine the two columns into a single column containing just 'M' and 'F'. Also, I would preferably like to do it in the SELECT statement I am writing while defining the column.
The result then should be something like:
ID Gender
1 M
2 F
3 F
4 M
5 F
I know I could achieve this with separate update statements like:
UPDATE table_1
SET Gender='M'
FROM table_2 t2
WHERE t2.Male=1
and
UPDATE table_1
SET Gender='F'
FROM table_2 t2
WHERE t2.Female=1
But I was really hoping to achieve the same result while declaring the Gender column?
Does anyone know if this is possible?
Thanks in advance!

You can use a CASE expression.
Query
UPDATE t1
SET t1.Gender = (
CASE WHEN t2.Male = 1 AND t2.Female = 0 THEN 'M'
WHEN t2.Male = 0 AND t2.Female = 1 THEN 'F'
ELSE NULL END
)
FROM Table_1 t1
JOIN Table_2 t2
ON t1.ID = t2.ID;

UPDATE table_1
SET Gender= CASE WHEN Male = 1 THEN 'M'
WHEN Female = 1 THEN 'F'
ELSE 'Other' // optional
END;
Of course im trying to be open mind and guess you allow Male = 0 and Female = 0
otherwise you can simplify with IIF
UPDATE table_1
SET Gender = IIF ( Male = 1, 'M', 'F' );

Here is how to do it with a SELECT statement:
SELECT ID,
CASE WHEN Male = 1 THEN 'M'
ELSE 'F' END AS gender
FROM My_Table

How about select if(Male = 1, 'M', 'F') as gender?

Related

SQL - Get per column count of differences when comparing two tables

I have 2 similar tables as shown below with minor difference between some cells
Table A
Roll_ID
FirstName
LastName
Age
1
AAA
XXX
31
2
BBB
YYY
32
3
CCC
ZZZ
33
Table B
Roll_ID
FirstName
LastName
Age
1
AAA
XXX
35
2
PPP
YYY
36
3
QQQ
WWW
37
I would like to get an output that shows the count of different records on a per-column level.
For example the output of the query for the above scenario should be
Output
Roll_ID
FirstName
LastName
Age
0
2
1
3
For this question we can assume that there will always be one column which will have non-null unique values (or one column which may be primary key). In above example Roll_ID is such a column.
My question is: What would be the most efficient way to get such an output? Is there anything to keep in mind when running such query for tables that may have millions of records from point of view of efficiency?
First you have to join the tables
SELECT *
FROM table1
JOIN table2 on table1.ROLL_ID = table2.ROLL_ID
Now just add the counts
SELECT
SUM(CASE WHEN table1.FirstName <> table2.FirstName THEN 1 ELSE 0 END) as FirstNameDiff,
SUM(CASE WHEN table1.LastName <> table2.LastName THEN 1 ELSE 0 END) as LastNameDiff,
SUM(CASE WHEN table1.Age <> table2.Age THEN 1 ELSE 0 END) as AgeDiff
FROM table1
JOIN table2 on table1.ROLL_ID = table2.ROLL_ID
If an id not existing in both tables is considered "different" then you would need something like this
SELECT
SUM(CASE WHEN COALESCE(table1.FirstName,'x') <> COALESCE(table2.FirstName,'y') THEN 1 ELSE 0 END) as FirstNameDiff,
SUM(CASE WHEN COALESCE(table1.LastName,'x') <> COALESCE(table2.LastName,'y') THEN 1 ELSE 0 END) as LastNameDiff,
SUM(CASE WHEN COALESCE(table1.Age,-1) <> COALESCE(table2.Age,-2) THEN 1 ELSE 0 END) as AgeDiff
FROM ( SELECT table1.Roll_id FROM table1
UNION
SELECT table2.Roll_id FROM table2
) base
LEFT JOIN table1 on table1.ROLL_ID = base.ROLL_ID
LEFT JOIN table2 on table2.ROLL_ID = base.ROLL_ID
Here we get all the roll_ids and then left join back to the tables. This is much better than a cross join if the roll_id column is indexed.
SELECT SUM(IIF(ISNULL(A.FirstName, '') <> ISNULL(B.FirstName, ''), 1, 0)) AS FirstNameRecordDiff,
SUM(IIF(ISNULL(A.LastName, '') <> ISNULL(B.LastName, ''), 1, 0)) AS LastNameRecordDiff,
SUM(IIF(ISNULL(A.Age, 0) <> ISNULL(B.Age, 0), 1, 0)) AS LastNameRecordDiff
FROM A
FULL OUTER JOIN B
ON B.Roll_ID = A.Roll_ID;
This query intentionally allows nulls to equal, assuming that a lack of data would mean the same thing to the end user.
As written, it would only work on SQL Server. To use it for MySQL or Oracle, the query would vary.

SQL AS query results in duplicate column

I have a SQL query like so
SELECT
name,
CASE WHEN (new_value=2) THEN 0 END as out,
CASE WHEN (previous_value=2) THEN 1 END as out
FROM my_table;
This results in duplicate columns:
name out out
foo 1 null
bar null 1
instead of
name out
foo 1
bar 0
How do I fix this?
You want one case expression with two conditions:
SELECT name,
(CASE WHEN new_value = 2 THEN 0
WHEN previous_value = 2 THEN 1
END) as out
FROM my_table;
Consider:
SELECT
name,
CASE
WHEN new_value = 2 THEN 0
WHEN previous_value = 2 THEN 1
END as out
FROM my_table;
In your query, each case expression generates one column in the resulset. You want only one, with two branches (denoted by when ... then ...)
You are getting null output, so you need to add else on this.
select name,
case
when new_value = 2 then 0
when previous_value = 2 then 1
else 0 end as out
from my_table;

Returning only id's of records that meet criteria

I need to return distinct ID's of records which meet following conditions :
must have records with field reason_of_creation = 1
and must NOT have records with field reason_of_creation = 0 or null
in the same time.
While i was able to do it, i keep wondering is there more elegant (even recommended) way of doing it.
Here is anonymized version of what i have :
select distinct st.some_id from (
select st.some_id, wanted.wanted_count as wanted, unwanted.unwanted_count as unwanted
from some_table st
left join (
select st.some_id, count(st.reason_of_creation) as wanted_count
from some_table st
where st.reason_of_creation=1
group by st.some_id
) wanted on wanted.some_id = st.some_id
left join (
select st.some_id, count(st.reason_of_creation) as unwanted_count
from some_table st
where st.reason_of_creation=0
group by st.some_id
) unwanted on unwanted.some_id = st.some_id
where wanted.wanted_count >0 and (unwanted.unwanted_count = 0 or unwanted.unwanted_count is null)
) st;
Sample data :
some_id reason_of_creation
1 1
1 0
2 1
3 null
4 0
4 1
5 1
desired result would be list of records with some_id = 2, 5
It seems to me your query is overkill,all you need is some post aggregation filtering
SELECT some_id FROM t
GROUP BY some_id
HAVING SUM(CASE WHEN reason_of_creation = 1 THEN 1 ELSE 0 END)>0
AND SUM(CASE WHEN reason_of_creation = 0 OR reason_of_creation IS NULL THEN 1 ELSE 0 END)=0
I think that more elegant query exists and it is based on assumption what reasoson_of_crdeation field is integer, so minimal possible it's value, which greater than 0 is 1
This is for possible negative values for reasoson_of_crdeation:
select someid from st
where reasoson_of_crdeation != -1
group by someid
having(min(nvl(abs(reasoson_of_crdeation), 0)) = 1)
or
select someid from st
group by someid
having(min(nvl(abs(case when reasoson_of_crdeation = -1 then -2 else reasoson_of_crdeation end), 0)) = 1)
And this one in a case if reasoson_of_crdeation is non-negative integer:
select someid from st
group by someid
having(min(nvl(reasoson_of_crdeation, 0)) = 1)

Interview : update table values using select statement

Interviewer asked me one question, which seems very easy, but I couldn't figure out, how to solve this
Name | Gender
--------------
A | F
B | M
C | F
D | F
E | M
From the above data, gender was wrongly entered, which means in place of F it should be M and in place of M it should F. How to update whole table with a single line sql query (don't use pl/sql block). Since, if I will update gender column one by one, then possible error would be all rows values of gender column becomes either F or M.
Final output should be
Name | Gender
--------------
A | M
B | F
C | M
D | M
E | F
Try this..
Update TableName Set Gender=Case when Gender='M' Then 'F' Else 'M' end
On OP request..update using Select...
Update TableName T Set Gender=(
Select Gender from TableName B where T.Gender!=B.Gender and rownum=1);
SQL FIDDLE DEMO
update table_name
set gender = case when gender = 'F' then 'M'
when gender = 'M' then 'F'
end
SQL works on Set theory principles, so updates are happening in parallel, you don't
need Temporary storage to store the values before overwriting like we do in
other programming language while swapping two values.
The right way to do such an update is as Amit singh first answered.
But if you really want to have a select statement in your update (have know idea why), then you can do something like this:
update table1 t
set Gender = (select case when i.Gender = 'F' Then 'M' else 'F' end
from table1 i
where i.Name = t.Name);
Here is a sqlfiddle demo
sql server example but same applies
declare #Table TABLE
(
Id int,
Value char(1)
)
insert into #Table
select 1, 'F'
union select 2, 'F'
union select 3, 'F'
union select 4, 'M'
union select 5, 'M'
union select 6, 'M'
select * from #Table
update #Table set Value = case when Value = 'F' then 'M' when Value = 'M' then 'F' else Value End
select * from #Table
you can try this:-
update [table] a
set Gender=(select case when gender='F' then 'M' else 'F' end from [table] b
where a.name=b.name)
above query will match the names and will update gender accordingly.

Returning 1 or 0 in specific SQL query

I have three columns in the table MYTABLE (ID, NUM, NAMES). There is a column NAMES. I need to check on NAMES column to see if the first name is JACK or BRUCE and the corresponding NUM column = 0. If the match is found, return 1 else 0.
ID NUM NAMES
1 1 'TOM'
2 1 'MIKE'
3 0 'JACK'
4 1 'MICKY'
5 0 'BRUCE'
I've came up with the following query:
select *
case NAMES in ('JACK', 'BRUCE') and NUM=0 then 1 else 0 end as MYNAMES
from MYTABLE;
That does not work unfortunately.
This works (SQLFiddle demo):
SELECT id, num,
CASE WHEN names IN ('JACK', 'BRUCE') AND num=0
THEN 1 ELSE 0 END AS mynames
FROM mytable
select case
when exists
(
select *
from YourTable
where name in ('JACK', 'BRUCE')
and NUM = 0
)
then 1
else 0
end
from dual
Live example at SQL Fiddle.
select case when NAMES in ('JACK','BRUCE') AND NUM = 0
then 1
else 0
end
from your_table