Find missing number from not sequence number - sql

I have a database with 5 columns (A1,A2,A3,A4,A5) which store 5 numbers.
The 5 numbers are "1,2,3,4,5".
A1 A2 A3 A4 A5
-------------------------------
2 4 5 Null Null
I want get the missing number which is "1" and "3".
How do I find the missing number from 5 numbers?

Select Replace(Replace(Replace(Replace(
Replace('12345',(Cast(Coalesce(A5,0) as varchar(1))),''),
(Cast(Coalesce(A4,0) as varchar(1))),''),
(Cast(Coalesce(A3,0) as varchar(1))),''),
(Cast(Coalesce(A2,0) as varchar(1))),''),
(Cast(Coalesce(A1,0) as varchar(1))),'') from Table1
Sql Fiddle Demo

You can do this
WITH sequence AS
(
SELECT 1 n UNION ALL
SELECT n + 1 FROM sequence WHERE n < 5
)
SELECT n
FROM sequence s LEFT JOIN table1 t
ON s.n IN (t.a1, t.a2, t.a3, t.a4, t.a5)
WHERE t.a1 IS NULL
Output:
| N |
|---|
| 1 |
| 3 |
Here is SQLFiddle demo

Depending on the desired output, this might work. This returns the relevant missing number(s) for each row.
SELECT CASE WHEN COALESCE(A1,0)<>1 AND COALESCE(A2,0)<>1 AND COALESCE(A3,0)<>1
AND COALESCE(A4,0)<>1 AND COALESCE(A5,0)<>1 THEN 1 ELSE '' END A
, CASE WHEN COALESCE(A1,0)<>2 AND COALESCE(A2,0)<>2 AND COALESCE(A3,0)<>2
AND COALESCE(A4,0)<>2 AND COALESCE(A5,0)<>2 THEN 2 ELSE '' END B
, CASE WHEN COALESCE(A1,0)<>3 AND COALESCE(A2,0)<>3 AND COALESCE(A3,0)<>3
AND COALESCE(A4,0)<>3 AND COALESCE(A5,0)<>3 THEN 3 ELSE '' END C
, CASE WHEN COALESCE(A1,0)<>4 AND COALESCE(A2,0)<>4 AND COALESCE(A3,0)<>4
AND COALESCE(A4,0)<>4 AND COALESCE(A5,0)<>4 THEN 4 ELSE '' END D
, CASE WHEN COALESCE(A1,0)<>5 AND COALESCE(A2,0)<>5 AND COALESCE(A3,0)<>5
AND COALESCE(A4,0)<>5 AND COALESCE(A5,0)<>5 THEN 5 ELSE '' END E
FROM NumTest
WHERE COALESCE(A1,0)+COALESCE(A2,0)+COALESCE(A3,0)+COALESCE(A4,0)+COALESCE(A5,0)<>15
The results look like:

you'll need a table of integers from 1 to (in this case) 5:
DECLARE #ints table (n int);
INSERT #ints VALUES (1), (2), (3), (4), (5);
second, we get the numbers in the table row into a single comparable set:
SELECT x INTO #all FROM (
SELECT A1 as x FROM myTable WHERE ID = myRow
UNION ALL
SELECT A2 as x FROM myTable WHERE ID = myRow
UNION ALL
SELECT A3 as x FROM myTable WHERE ID = myRow
UNION ALL
SELECT A4 as x FROM myTable WHERE ID = myRow
UNION ALL
SELECT A5 as x FROM myTable WHERE ID = myRow
) y
then you can derive the answer:
SELECT #ints.n
FROM #ints left join #all on #ints.n = #all.x
WHERE #all.x is null
ORDER BY 1

Related

How to get specific records in posgtres

In Postgres I have two tables:
Table A { int keyA, Text name}
Table B { int keyB, int keyA, char mark, date start, date end}
Mark from Table B could be 'X', 'Y', 'Z'.
I want to get every record 'X' with dates but only one from 'Y', 'Z'. Also if there are 'X', 'Y', 'Z' i want only 'X'.
From:
keyB
keyA
mark
start
end
1
1
X
15-01-2023
16-01-2023
2
1
X
17-01-2023
18-01-2023
3
1
Y
null
null
4
1
Z
null
null
5
2
Y
null
null
6
2
Z
null
null
7
2
Y
null
null
8
3
Z
null
null
9
3
Y
null
null
10
4
X
19-01-2023
20-01-2023
I want to get
keyB
keyA
mark
start
end
1
1
X
15-01-2023
16-01-2023
2
1
X
17-01-2023
17-01-2023
5
2
Y
null
null
8
3
Z
null
null
10
4
X
19-01-2023
20-01-2023
I tried:
1.
Select A.name,
(select b2.start from B b2 where b2.keyA = A.keyA and b2.mark = 'X') as Start,
(select b2.end from B b2 where b2.keyA = A.keyA and b2.mark = 'X') as End,
from A order by name;
Order is important. I need to have name first.
There is a porblem. In subqueries i have more than one record so i have to add limit 1. But I want to get every X not only one.
If I do this
Select A.name, B.start, B.end
from A inner join B on A.keyA = B.keyB
I'll have X, Y, Z and as I mentioned I want only X or one from Y or Z.
Any idea how should I solve this?
Use the row_number function with your join query as the following:
select name, keyB, keyA, mark, start_dt, end_dt
from
(
select A.name, B.*,
row_number() over (partition by B.keyA order by case when B.mark='X' then 1 else 2 end, B.keyb) rn
from tableB B join tableA A
on B.keyA = A.keyA
) T
where mark = 'X' or rn = 1
order by keyb
See demo

How to Generate 2 Levels Loop

If I have 2 variables
A = 2
B = 3
I want to generate result like this
A | B | Text
1 | 1 | Text1
1 | 2 | Text2
1 | 3 | Text3
2 | 1 | Text4
2 | 2 | Text5
2 | 3 | Text6
I try to Google and can achieved 1 level with this query
declare #start int = 1
declare #end int = 3
;with numcte
AS
(
SELECT #start as [SEQUENCE]
UNION all
SELECT [SEQUENCE] + 1
FROM numcte WHERE [SEQUENCE] < #end
)
SELECT [SEQUENCE], 'text' + CAST([SEQUENCE] as varchar) as [text] FROM numcte
How can I achieve 2 levels loop?
One rather simple method is:
select a.a, b.b, concat('text', row_number() over (order by a, b))
from (values (1), (2)) a(a) cross join
(values (1), (2), (3)) b(b);
Or if you really want to declare variables:
declare #a int = 2;
declare #b int = 3;
with n as (
select 1 as n union all
select n + 1
from n
where n < #a or n < #b
)
select na.n as a, nb.n as b, concat('text', row_number() over (order by na.n, nb.n))
from n na join
n nb
on na.n <= #a and nb.n <= #b;
Here is a db<>fiddle.
Use a numbers table (many examples you can use by searching). One way to produce what is likely a vastly simplified example is:
with cte as (
select 1 as num
union all select num + 1 from cte where num < 3 )
select cte.num, cte2.num from cte
cross join cte as cte2
where cte.num in (1, 2)
order by cte.num, cte2.num
;
Work through that - it may look daunting. Start thinking in terms of sets! Fiddle

Complicated SQL query request

I have a table
Number Letter KeyLetter
1 a 1
1 b 0
1 c 0
1 d 0
2 e 0
2 f 0
2 g 0
3 h 1
3 i 1
3 j 0
From it I want this:
Number Letter KeyLetter
1 a 1
2 e 0
2 f 0
2 g 0
3 h 1
3 i 1
For each set of numbers, if a letter is a KeyLetter, I want to ignore any non KeyLetters.
If a set of numbers doesn't have an entry where the letter is a KeyLetter, then show all of the entries in that set of numbers.
What SQL query would be able to do this?
Simple answer, return the rows with KeyLetter = 1, and also those with a Number not having a KeyLetter = 1.
select *
from tablename t1
where t1.KeyLetter = 1
or not exists (select * from tablename t2
where t1.Number = t2.Number
and t2.KeyLetter = 1)
Alternatively:
select t1.*
from tablename t1
join (select Number, max(KeyLetter) maxKeyLetter
from tablename
group by Number) t2
on t1.Number = t2.Number and t1.KeyLetter = t2.maxKeyLetter
Or...
select *
from tablename
where (Number, KeyLetter) in
(select Number, max(KeyLetter)
from tablename
group by Number)
The first two are Core ANSI SQL compliant. The latter one uses extension F641, "Row and table constructors".

Merge multiple columns into one column with multiple rows

In PostgreSQL, how can I merge multiple columns into one column with multiple rows?
The columns are all boolean, so I want to:
Filter for true values only
Replace the true value (1) with the name of the column (A, B or C)
I have this table:
ID | A | B | C
1 0 1 0
2 1 1 0
3 0 0 1
4 1 0 1
5 1 0 0
6 0 1 1
I want to get this table:
ID | Letter
1 B
2 A
2 B
3 C
4 A
4 C
5 A
6 B
6 C
I think you need something like this:
SELECT ID, 'A' as Letter FROM table WHERE A=1
UNION ALL
SELECT ID, 'B' as Letter FROM table WHERE B=1
UNION ALL
SELECT ID, 'C'as Letter FROM table WHERE C=1
ORDER BY ID, Letter
SELECT ID,
(CASE
WHEN TABLE.A = 1 then 'A'
WHEN TABLE.B = 1 then 'B'
WHEN TABLE.C = 1 then 'C'
ELSE NULL END) AS LETTER
from TABLE
You may try this.
insert into t2 select id, 'A' from t1 where A=1;
insert into t2 select id, 'B' from t2 where B=1;
insert into t2 select id, 'C' from t3 where C=1;
If you care about the order, then you can do this.
insert into t3 select id, letter from t2 order by id, letter;
W/o UNION
You can use a single query to get the desired output.Real time example
select id
,regexp_split_to_table((
concat_ws(',', case
when a = 0
then null
else 'a'
end, case
when b = 0
then null
else 'b'
end, case
when c = 0
then null
else 'c'
end)
), ',') l
from c1;
regexp_split_to_table() & concat_ws()

Use a standard single sql to group data (SQL Server)

Raw data with 2 columns:
0 33
2 null
0 44
2 null
2 null
2 null
0 55
2 null
2 null
.....
Results I want:
2 33
2 44
2 44
2 44
2 55
2 55
....
Can I use a SQL statement to accomplish this? (return the rows with 2 only but fill with values come from the previous row that is 0), there could be many '2 null' between 0.
This way
with s as (
select *
from
(values
(1,0,33 ),
(2,2,null),
(3,0,44 ),
(4,2,null),
(5,2,null),
(6,2,null),
(7,0,55 ),
(8,2,null),
(9,2,null)
) T(id,a,b)
)
select s1.a, t.b
from s s1
cross apply (
select top(1) s2.b
from s s2
where s2.id < s1.id and s2.b is not null and s2.a = 0
order by s2.id desc ) t
where s1.a = 2
order by s1.id;
I use CROSS APPLY so the query may be easily extended to get other columns from the relevant '0' row.
First of all, select value for every row with null:
SELECT col2 FROM (SELECT MAX(ID) FROM your_tbl t WHERE t.ID < ID AND col2 IS NOT NULL);
Then write a condition for your table with that subquery:
SELECT col1, (
SELECT col2 FROM your_tbl where id = (SELECT MAX(ID) FROM your_tbl t
WHERE t.ID < tbl.ID AND col2 IS NOT NULL))
FROM your_tbl tbl WHERE col1 <> 0;