Map column values in SQL - sql

I have a table named table1, with one column named col1, which takes value in range 1-9.
table1
col1
1
9
7
2
4
6
1
9
3
5
Now I want to add another column which maps values in col1 to another value given in a map.
1 -> A, 2 -> B, 3 -> C, 4 -> D, 5 -> E, 6 -> F, 7 -> G, 8 -> H, 9 -> I
I want results to look like below.
col1 col2
1 A
9 I
7 G
2 B
4 D
6 F
1 A
9 I
3 C
5 E
My approach is to create a new table with mapping and then do a inner join.
CREATE TABLE map (
col1 int,
col2 varchar
);
INSERT INTO map
(col1, col2)
VALUES
(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E'),(6,'F'),(7,'G'),(8,'H'),(9,'I');
SELECT table1.col1, map.col2
FROM table1 INNER JOIN map ON table1.col1 = map.col1
Is this efficient approach, are there better methods than this?

Use case statement:
Update t
Update new_column =
Case
WHEN col1 = 1 THEN 'A'
WHEN col1 = 2 THEN 'B'
WHEN col1 = 3 THEN 'C'
WHEN col1 = 4 THEN 'D'
WHEN col1 = 5 THEN 'E'
WHEN col1 = 6 THEN 'F'
WHEN col1 = 7 THEN 'G'
WHEN col1 = 8 THEN 'H'
WHEN col1 = 9 THEN 'I'
END
FROM table1 t

Although creating map table can be a good idea, assume you need to fill the map table with query. Then case statement is useful.
CASE col1
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
WHEN 3 THEN 'C'
WHEN 4 THEN 'D'
-- And so on
END

Related

drop duplicates on some columns and keep other columns values

I have the following table with Postgres:
Id Col1 Col2 Col3
1 A 1 x
2 A 0 y
3 A 0 z
4 B 0 x
5 B 1 y
6 C 0 z
As part of a select query, I want to be able to drop duplicates in Col1, based on the highest Col2 values (where will never be multiple highest values per Col1 value), and keep the corresponding Col2, Col3 values.
Desired output:
Id Col1 Col2 Col3
1 A 1 x
5 B 1 y
6 C 0 z
In Postgres, you can use distinct on:
select distinct on (col1) t.*
from t
order by col1, col2 desc;

Count records in query in groups based on column value

Let's suppose a have a very simple query in SQL
SELECT Col1,Col2 From Table1
and it gives me result:
Col1 Col2
A 5
A 7
A 2
B 1
B 1
B 4
B 0
C 4
C 1
C 2
I want to count rows in groups made by Col1 and in order made by Col2. If values in Col2 for some rows in group are equal then they should have different numbers, as shown in example
So I want to have
Col1 Col2 Nr
A 5 2
A 7 3
A 2 1
B 0 1
B 1 2
B 1 3
B 4 4
C 4 3
C 1 1
C 2 2
Any ideas how to make it?
If your database supports window functions, use ROW_NUMBER
select col1,col2,row_number() over(partition by col1 order by col2) as nr
from tablename
If your database doesn't support window functions, use
select col1,col2,
(select count(*)+1 from tablename t1 where t1.col1=t.col1 and t1.col2<t.col2) as nr
from tablename t
You can use the row_number window function:
SELECT col1,
col2,
ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2 ASC) AS Nr
FROM table1
ORDER BY 1, 2, 3

Need help in Sql query joining operation

I have below test table with data:
Create table test
(
col1 int,
col2 int
)
Sample data:
col1 col2
-------------
1 4
1 5
2 4
3 5
3 4
Now I want all the col1 which have col2 value 4 and 5
o/p 1,3 since it contain 4 as well as 5 col2 value
SELECT *
FROM TestTable
WHERE col1 IN
(
SELECT col1
FROM TestTable
WHERE col2 = 4
)
AND col2 = 5
If you only need col1 value, you can do
of course, the value in the having clause (2 in this case) depends on the numbers of elements in the IN clause (so if you have 3, 4, 5, you'll need to put 3 in the having clause)
select col1 from test t1
where col2 in (4, 5)--take only results with 4 and 5 in col2
group by col1
having count(distinct col2) = 2 --be sure there's at least a 4 and a 5

Query without Union operator SQL

TABLE X
col1,col2
1 , 2
1 , 7
1 , 4
1 , 8
2 , 3
2 , 1
2 , 2
3 , 1
3 , 8
3 , 9
3 , 4
4 , 5
4 , 3
4 , 2
4 , 8
4 , 4
I want to retrieve the col1 values that contains in the col2 the values 2 and 4
in this case it will retrieve the values 1 and 4
How can i accomplish this without using the UNION ALL operator ?
The query that i am using is
select distinct col1
from X as A
where col1 = (
select col1 from (
select distinct col1
from X as B
where A.col1 = B.col1 and col2 = 2
union ALL
select distinct col1
from X as C
where A.col1 = C.col1 and col2 = 4
) D
group by col1
having count(col1) > 1
)
It is returning the correct result but i guess is to performance expensive.
Can anyone give me ideas about how to achieve the same result but without unions ?
This problem is called Relational Division, here is one way to do so:
SELECT col1
FROM tablex
WHERE col2 IN (2, 4)
GROUP BY col1
HAVING COUNT(DISTINCT col2) >=2
The HAVING COUNT(col2) >=2 will ensure that the selected col1 must have both the two values 2 and 4 at least.
SQL Fiddle Demo
I think the best performance will come from inner joining the table with itself:
SELECT DISTINCT X1.col1
FROM X X1 INNER JOIN X X2 ON X1.col1=X2.col1
WHERE X1.col2=2 AND X2.col2=4

SQL: return only those columns with different data

I have two rows from a table that has many columns. How do I return only those columns where the value for row1 does not equal the value for row2?
I'm using Oracle 11.1.0.07
~~ Edit: clarification ~~
Example:
So I've got a table with rows:
1 a b c d e f g h i j k l
2 a x c d e x g h y j k l
3 a b x d e x g h x y k z
I want to return rows where id (first column) is 1 or 3, only those columns that are different. So:
1 c f i j l
3 x x x y z
with column names.
In reality, the table I'm pulling from has 223007 rows, and 40 columns. The above is a simplified example. There are two rows (one each for primary key values) that I'm wanting to compare.
First, the SQL language was not designed for dynamic column generation. For that, you need to write dynamic SQL which should be done in a middle-tier or reporting component.
Second, if what you seek is to compare two specific rows, then the simplest solution would probably be to return those rows and analyze them in a middle-tier component. However, if you accept that we must return all columns and you insist on doing this in SQL, this is one solution:
With Inputs As
(
Select 1 As Col1,'a' As Col2,'b' As Col3,'c' As Col4,'d' As Col5,'e' As Col6,'f' As Col7,'g' As Col8,'h' As Col9,'i' As Col10,'j' As Col11,'k' As Col12,'l' As Col13
Union All Select 2,'a','x','c','d','e','x','g','h','y','j','k','l'
Union All Select 3,'a','b','x','d','e','x','g','h','x','y','k','z'
)
, TransposedInputs As
(
Select Col1, 2 As ColNum, Col2 As Value From Inputs
Union All Select Col1, 3, Col3 From Inputs
Union All Select Col1, 4, Col4 From Inputs
Union All Select Col1, 5, Col5 From Inputs
Union All Select Col1, 6, Col6 From Inputs
Union All Select Col1, 7, Col7 From Inputs
Union All Select Col1, 8, Col8 From Inputs
Union All Select Col1, 9, Col9 From Inputs
Union All Select Col1, 10, Col10 From Inputs
Union All Select Col1, 11, Col11 From Inputs
Union All Select Col1, 12, Col12 From Inputs
Union All Select Col1, 13, Col13 From Inputs
)
, UniqueValues As
(
Select Min(Col1) As Col1, ColNum, Value
From TransposedInputs
Where Col1 In(1,3)
Group By ColNum, Value
Having Count(*) = 1
)
Select Col1
, Min( Case When ColNum = 2 Then Value End ) As Col2
, Min( Case When ColNum = 3 Then Value End ) As Col3
, Min( Case When ColNum = 4 Then Value End ) As Col4
, Min( Case When ColNum = 5 Then Value End ) As Col5
, Min( Case When ColNum = 6 Then Value End ) As Col6
, Min( Case When ColNum = 7 Then Value End ) As Col7
, Min( Case When ColNum = 8 Then Value End ) As Col8
, Min( Case When ColNum = 9 Then Value End ) As Col9
, Min( Case When ColNum = 10 Then Value End ) As Col10
, Min( Case When ColNum = 11 Then Value End ) As Col11
, Min( Case When ColNum = 12 Then Value End ) As Col12
, Min( Case When ColNum = 13 Then Value End ) As Col13
From UniqueValues
Group By Col1
Results:
Col1 | Col2 | Col3 | Col4 | Col5 | Col6 | Col7 | Col8 | Col9 | Col10 | Col11 | Col12 | Col13
1 | NULL | NULL | c | NULL | NULL | f | NULL | NULL | i | j | NULL | l
3 | NULL | NULL | x | NULL | NULL | x | NULL | NULL | x | y | NULL | z
If you're trying to transpose or pivot your row1 and row2 into columns, then these questions might help you:
Oracle PIVOT, twice?
Oracle SQL pivot query
etc (Google for Oracle PIVOT Query)
After pivoting, you can select only those tuples that have row1_pivoted <> row2_pivoted
Hmm. first stab at an answer was wrong when I re-read the question. So... for clarification, you've got some rows/values
a b c
d e f
a b c
and you'd like only the 'd e f' row returned, because it doesn't have a duplicate row elsewhere?
The number of columns in the result set can't be dynamic (without resorting to dynamic SQL).
You might be interested in the Unpivot operator. That would let you return the columns as rows.
I haven't experiemented with it myself yet, so unfortunately I'm unable to help you with it :/
Edit
I wanted to give manual pivoting a shot :)
select *
from inputs;
ID C1 C2 C3 C4 C5 C6
--- -- -- -- -- -- --
1 a b c d e f
2 a x c d e x
3 a b x d e x
with unpivoted as(
select id, 'c1' as cn, c1 as cv from inputs union all
select id, 'c2' as cn, c2 as cv from inputs union all
select id, 'c3' as cn, c3 as cv from inputs union all
select id, 'c4' as cn, c4 as cv from inputs union all
select id, 'c5' as cn, c5 as cv from inputs union all
select id, 'c6' as cn, c6 as cv from inputs
)
select cn
,max(case when id = 1 then cv end) as id1
,max(case when id = 3 then cv end) as id3
from unpivoted
where id in(1,3)
group
by cn
having count(distinct cv) = 2;
CN ID1 ID3
-- --- ---
c3 c x
c6 f x
The above works by creating one row for each column and ID (2 * 6 = 12 rows).
Then I group by the column name (assigned as a literal).
I will always get 6 groups (one for each column). In each group I will have exactly two rows (one for each selected ID).
In the having clause, I count the number of unique values for the column. If the rows have the same value, then the numner of unique values = 1. Else we have a mismatch.
Note 1. id in(x,y) is pushed into the view, so we are not selecting the entire table.
Note 2. This cannot be extended into comparing more than 2 rows.
Note 3. This does not deal with NULLS in either column