finding consecutive numbers - sql

ok - so i searched the internet for this, and none of the examples i found are exactly like mine.
i have a table with 5 columns and thousands of rows.
i need to find consecutive numbers within each row. i need to end up with 3 queries for the situations shown below
n1 n2 n3 n4 n5
=======================
1 3 4 6 9 = should result in 1 (when checking for pairs)
1 3 4 5 9 = should result in 1 (when checking for triplets)
1 2 5 8 9 = should result in 1 (when checking for double pairs)
This is what i have to move the columns into rows, but i am not sure how to check this now.
select n1 from (
select n1 from myTable where Id = 1
union all select n2 from myTable where Id = 1
union all select n3 from myTable where Id = 1
union all select n4 from myTable where Id = 1
union all select n5 from myTable where Id = 1
) t
order by n1
Thank you for all your help!
#TimBiegeleise, update :
so i found this on google for Gaps & Islands:
SELECT ID, StartSeqNo=MIN(SeqNo), EndSeqNo=MAX(SeqNo)
FROM (
SELECT ID, SeqNo
,rn=SeqNo-ROW_NUMBER() OVER (PARTITION BY ID ORDER BY SeqNo)
FROM dbo.GapsIslands) a
GROUP BY ID, rn;
this is my updated query converting the columns to rows (but it requires 2 statements, i much rather have 1) and implementing the island part - but i don't understand how that give me the result what i need (see above). below i show the original row data and the result.
select n1, IDENTITY (INT, 1, 1) AS ID
into #test
from (
select n1 from myTable where Id = 8
union all select n2 from myTable where Id = 8
union all select n3 from myTable where Id = 8
union all select n4 from myTable where Id = 8
union all select n5 from myTable where Id = 8
) as t
order by n1
SELECT ID, StartSeqNo=MIN(n1), EndSeqNo=MAX(n1)
FROM (
SELECT ID, n1
,rn=n1-ROW_NUMBER() OVER (PARTITION BY ID ORDER BY n1)
FROM #test) a
GROUP BY ID, rn
drop table #test
original row - should return 1 (when checking for "pair"/consecutive numbers
n1 n2 n3 n4 n5
=======================
31 27 28 36 12
the result i get with the above query:
StartSeqNo EndSeqNo
1 12 12
2 27 27
3 28 28
4 31 31
5 36 36
help :-) !

ok, i got it. this query returns a value of 1 for the above stated row
select COUNT(*) as pairs
from (
SELECT StartSeqNo=MIN(n1), EndSeqNo=MAX(n1)
FROM (
SELECT n1, rn=n1-ROW_NUMBER() OVER (ORDER BY n1)
from (
select n1 from myTable where Id = 8
union all select n2 from myTable where Id = 8
union all select n3 from myTable where Id = 8
union all select n4 from myTable where Id = 8
union all select n5 from myTable where Id = 8
) t
) x
GROUP BY rn
) z
where StartSeqNo+1 = EndSeqNo

Related

Split one row to many in same database table

We have a requirement where we want to split one row to many rows ( in the same table ) based on some conditions.
Let's suppose we have this table :
ID
Value
1
V1
2
V2
3
V3
Requirement is,
if ID=1, split this row into two more rows where IDs of new rows will be 4 and 5 and the value will be V1 (same as ID = 1 value) only.
if ID=2, don't split.
if ID=3, split this row into one more row where ID of the new row will be 6 and value will be V3 (same as ID = 3 value) only.
The final o/p will be :
ID
Value
1
V1
4
V1
5
V1
2
V2
3
V3
6
V3
I am looking out for some SQL script/Stored Proc that will help me in achieving the same.
You can generate the rows with a join and derived table . . . and then use union all to bring in the existing rows:
select id, value
from t
union all
select x.new_id, t.value
from t join
(select 1 as old_id, 4 as new_id from dual union all
select 1 as old_id, 5 as new_id from dual union all
select 3 as old_id, 6 as new_id from dual
) x
on t.id = x.old_id;
If you just want to insert the values, use insert with the second query.
You can join your table with numbers as follows:
select case when t.id = 2 then t.id
when t.id = 3 then t.id * lvl
when t.id = 1 and lvl > 1 then lvl+2
else lvl
end as id, t.value
from your_table t
cross join (select level as lvl from dual connect by level <=3)
where t.id = 1 or (t.id=2 and lvl=1) or (t.id = 3 and lvl <= 2)

SQL Query: Count the number of distinct values in a table

I am trying to create a SQL query to count the number of distinct values in a SQL table.
My table has 6 columns:
n1 n2 n3 n4 n5 n6
______________________
3 5 7 9 11 20
3 7 11 15 17 20
3 15 26 28 30 40
15 26 30 40 55 56
3 4 5 9 15 17
17 20 26 28 30 40
And here's the result I am trying to get:
value frequency
______________________
3 4
4 1
5 2
7 2
9 2
11 2
15 3
17 3
20 3
26 3
28 2
30 3
40 3
55 1
56 1
So basically, I need the query to look at the whole table, take a note of each value that appears, and count the number of times that particular value appears.
Use UNION ALL to get all the nX column values in 1 column and aggregate:
select value, count(*) as frequency
from (
select n1 as value from tablename union all
select n2 from tablename union all
select n3 from tablename union all
select n4 from tablename union all
select n5 from tablename union all
select n6 from tablename
) t
group by value
I would recommend cross apply for this purpose:
select v.n, count(*) as frequency
from t cross apply
(values (n1), (n2), (n3), (n4), (n5), (n6)) v(n)
group by v.n;
cross apply, which implements a lateral join is more efficient than union all for unpivoting data. This is particularly true if your "table" is really a view or complex query.
here is the beautiful use case of UNPIVOT if you are using SQL SERVER or ORACLE:
SELECT
[value]
, count(*) frequency
FROM
( select n1,n2,n3,n4,n5,n6 from tablename) p
UNPIVOT ([value] for nums in ( n1,n2,n3,n4,n5,n6 )) as unpvt
GROUP BY [value]
ORDER BY frequency DESC
which is more efficient than Union , if performance matters there.

compare numbers

so here is the deal, and all google search i could find has different scenarios, but nothing matching my situation :-).
this will be coded in VB.net, but since its sql syntax i can easily build it into vb later.
so i have a table from which i will grab a row with numbers:
select n1, n2, n3, n4, n5 from TempTable
essentially this will come back with something like the 3, 8, 12, 5, 33
i now have a 2nd table with the same columns (n1...n5)
i need to find which rows in the 2nd table which have more or equal than 2 numbers matching from the 1st set of numbers (3 or 8 or 12 or 5 or 33).
so if the 2nd table would look like this:
4, 3, 57, 33, 1
5, 6, 87, 21, 44
65, 3, 12, 7, 8
then the query should return 2 (1st and last row)
i hope that makes sense :-)
Thank you for your help!
I would first normalize the 2 tables, so that there is only an ID and the value (and maybe the position if that may be relevant). Let's call them A2 and B2:
SELECT ID, 1 AS Pos, N1 AS Value FROM A
UNION ALL
SELECT ID, 2 AS Pos, N2 AS Value FROM A
UNION ALL
SELECT ID, 3 AS Pos, N3 AS Value FROM A
UNION ALL
SELECT ID, 4 AS Pos, N4 AS Value FROM A
UNION ALL
SELECT ID, 5 AS Pos, N5 AS Value FROM A
Then get only the records with 2 or more matches:
SELECT B2.ID, COUNT(*) AS Matches FROM A2
INNER JOIN B2 ON A2.Value = B2.Value
GROUP BY B2.ID
HAVING COUNT(*) >= 2
And finally count them:
SELECT COUNT(*) FROM ...
In a single query that could look like this (but I recommend you make views for A2 and B2):
WITH A2 AS (
-- Normalize table 1
SELECT ID, 1 AS Pos, N1 AS Value FROM A
UNION ALL
SELECT ID, 2 AS Pos, N2 AS Value FROM A
UNION ALL
SELECT ID, 3 AS Pos, N3 AS Value FROM A
UNION ALL
SELECT ID, 4 AS Pos, N4 AS Value FROM A
UNION ALL
SELECT ID, 5 AS Pos, N5 AS Value FROM A
), B2 AS (
-- Normalize table 2
SELECT ID, 1 AS Pos, N1 AS Value FROM B
UNION ALL
SELECT ID, 2 AS Pos, N2 AS Value FROM B
UNION ALL
SELECT ID, 3 AS Pos, N3 AS Value FROM B
UNION ALL
SELECT ID, 4 AS Pos, N4 AS Value FROM B
UNION ALL
SELECT ID, 5 AS Pos, N5 AS Value FROM B
)
-- Count them
SELECT COUNT(*) FROM (
-- Get only the ones with 2 or more matches
SELECT B2.ID, COUNT(*) AS Matches FROM A2
INNER JOIN B2 ON A2.Value = B2.Value
GROUP BY B2.ID
HAVING COUNT(*) >= 2
) AS T

Generate numbers for given ranges (sql query)

I'm writing a query which generate numbers between specified ranges.
I have a table
NUM_RANGES
ID START_NUMBER END_NUMBER
-- ------------ ----------
1 1 5
2 6 10
I need to get this result:
ID NUMBER
-- ------
1 1
1 2
1 3
1 4
1 5
2 6
2 7
2 8
2 9
2 10
With this query i can get correct result but only for specified id in where clause:
select id, start_number + level - 1 next_tag
from (select id, start_number,end_number
from NUM_RANGES
where id = 1
)
connect by level <= end_number - start_number + 1
Without "where id = 1" im getting 62 rows with duplicates where distinct helps but with bigger ranges 1 - 200 , 200- 500 it works too slow ..
Thanks for help!
On Oracle 12c you can use CROSS APPLY:
select *
from num_ranges
cross apply(
select level - 1 + start_number as my_new_number
from dual
connect by level <= end_number - start_number + 1
);
In Oracle 11.2 and earlier, you can use a hierarchical query:
with
num_ranges ( id, start_number, end_number ) as (
select 1, 1, 5 from dual union all
select 2, 9, 12 from dual
)
-- End of simulated input data (for testing purposes only, not part of the solution).
-- SQL query begins below this line.
select id, start_number + level - 1 as nmbr
from num_ranges
connect by level <= end_number - start_number + 1
and prior id = id
and prior sys_guid() is not null
order by id, nmbr -- If needed
;
ID NMBR
---------- ----------
1 1
1 2
1 3
1 4
1 5
2 9
2 10
2 11
2 12
Specifically, without linking new rows to existing ones by the same id, you are generating an insane number of unnecessary rows. This is why you need prior id = id. The additional condition, prior sys_guid() is not null, is needed so that Oracle does not see cycles where it shouldn't see them (which are caused precisely by "prior id = id").
In Oracle 12.1 or higher, you can use the lateral clause:
select n.id, l.nmbr
from num_ranges n,
lateral ( select start_number + level - 1 as nmbr from dual
connect by level <= end_number - start_number + 1 ) l
order by id, nmbr -- If needed
;
This should work. Pretty fast too.
with cte as (select 0 as c from dual)
, cte4 as (select c from cte union all select c from cte union all select c from cte union all select c from cte)
, cte256 as (select t0.c from cte4 t0, cte4 t1, cte4 t2, cte4 t3)
, nums as (select row_number() over(order by null) as n from cte256 t0, cte256 t1, cte256 t2)
select NR.id, nums.n as NUMBER_
from nums
join NUM_RANGES NR on nums.n between NR.START_NUMBER and NR.END_NUMBER
;

sql server : count records

I have a tableA (ID int, Match varchar, tot int)
ID Match Tot
1 123
2 123
3 12
4 12
5 4
6 12
7 8
Now, I want to calculate Tot which is total number of match exists in the table. for example 123 occured twice, 12 exist thrice and so on. Also note that I want the count only at first match. here is the expected result.:
ID Match Tot
1 123 2
2 123
3 12 3
4 12
5 4 1
6 12
7 8 1
Another case:
ID Match Count Tot
1 123 2
2 123 1
3 12 10
4 12 10
5 4 3
6 12 5
7 8 7
Now I want to add the count for the same match. expected result:
ID Match Count Tot
1 123 2 3
2 123 1
3 12 10 25
4 12 10
5 4 3 3
6 12 5
7 8 7 7
Thanks
WITH tableA(ID, Match) AS
(
SELECT 1,123 UNION ALL
SELECT 2,123 UNION ALL
SELECT 3,12 UNION ALL
SELECT 4,12 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,12 UNION ALL
SELECT 7,8
)
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Match ORDER BY ID) = 1
THEN COUNT(*) OVER (PARTITION BY Match)
END AS Tot
FROM tableA
ORDER BY ID
SELECT match, COUNT(match ) as Tot
FROM tableA
GROUP BY match
Solution 1:
DECLARE #MyTable TABLE
(
ID INT PRIMARY KEY
,Match VARCHAR(10) NOT NULL
,Tot INT NULL
);
INSERT #MyTable(ID, Match)
SELECT 1, 123
UNION ALL
SELECT 2, 123
UNION ALL
SELECT 3, 12
UNION ALL
SELECT 4, 12
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, 12
UNION ALL
SELECT 7, 8;
--SELECT
SELECT *
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a;
--UPDATE
WITH MyCTE
AS
(
SELECT a.Tot
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a
)
UPDATE MyCTE
SET Tot = TotCalculated;
SELECT *
FROM #MyTable;
Solution 2:
UPDATE #MyTable
SET Tot = NULL;
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
ORDER BY x.ID
UPDATE #MyTable
SET Tot = t.Num
FROM #MyTable z
INNER JOIN
(
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
) t ON z.ID = t.ID;
SELECT *
FROM #MyTable;