Repeating rows based on the frequency - sql

I have a table with 2 columns named A and B which is defined as :
A B
c1 2
c2 3
c3 4
The expected output is :
A B
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4

CREATE TABLE #table2
([A] varchar(2), [B] int)
;
INSERT INTO #table2
([A], [B])
VALUES
('c1', 2),
('c2', 3),
('c3', 4)
;WITH nums AS
(SELECT 1 AS value ,a,b from #table2
UNION ALL
SELECT value + 1 AS value ,A,b
FROM nums
WHERE nums.value <B)
SELECT a,value
FROM nums order by a,value
output
a value
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4

Created a Table valued function in which i used Recursive cte to evaluate recurring value of given in put Then Join the table with function using Cross Apply
CREATE FUNCTION [dbo].[udf_GetData] (
#Data INT
)
RETURNS #output TABLE (
Data INT
)
BEGIN
;WITH CTe
AS
(
SELECT 1 As Data
UNION ALL
SELECT Data+1
FROM CTe
WHERE Data < #Data
)
INSERT INTO #output
SELECT Data FROM CTe
RETURN
END
Sample Data and explained How to call function using CROSS APPLY
DECLARE #Data AS TABLE (A VARCHAR(10),B INT)
INSERT INTO #Data
SELECT 'c1', 2 UNION ALL
SELECT 'c2', 3 UNION ALL
SELECT 'c3', 4
SELECT d.A,
(SELECT [dbo].[udf_GetData](d.B)) AS RecData
FROM #Data d
Result
A RecursiveData
----------------
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4

You could try this:
// test data
declare #tbl table(A char(2), B int);
insert into #tbl values
('c1', 2),
('c2', 3),
('c3', 4);
// create CTE with numbers which we will need to join
declare #max int;
select #max = max(B) from #tbl;
;with numbers as (
select 1 n
union all
select n + 1 from numbers
where n < #max
)
// join numbers with your table
select A, n from #tbl t
join numbers n on t.B >= n.n
order by A, n

Say, your table name is test.
WITH r(a, b, repeat) as
(SELECT a, b, 1 from test
union all
select a, b, repeat+1 from r
where r.repeat < r.b)
select * from r
ORDER BY a, repeat;

Related

postgresql - count distinct combination of three columns- order doesn't matter

I'm trying to count distinct combinations of three columns, order of the columns doesn't matter
sample :
a a a
a a b
a b a
b b a
b a b
the result I'm getting :
a a a 1
a a b 1
a b a 1
b b a 1
b a b 1
desired result
aaa 1
aab 2
bba 2
You can use an ordered array
select v[1], v[2], v[3], count(*) n
from tbl t
cross join lateral (
select array_agg(col order by col) v
from (
values (c1),(c2),(c3)
) t(col)
) s
group by v[1], v[2], v[3];
db<>fiddle
Maybe you can use checksums for getting the required result eg if it is really just combinations 'a' and 'b' that you are dealing with, you could convert the letters to integers (by calling the ASCII() function) and add these up so that you get a checksum.
TABLE
create table t (c1, c2, c3 ) as
select 'a', 'a', 'a' union all
select 'a', 'a', 'b' union all
select 'a', 'b', 'a' union all
select 'b', 'b', 'a' union all
select 'b', 'a', 'b' ;
Checksums
select c1, c2, c3, ascii( c1 ) + ascii( c2 ) + ascii( c3 ) as checksum
from t ;
-- output
c1 c2 c3 checksum
a a a 291
a a b 292
a b a 292
b b a 293
b a b 293
If this works for you, then you can use window functions eg
select c1, c2, c3, rc_ as rowcount
from (
select c1, c2, c3
, count(*) over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rc_
, row_number() over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rn_
from t
) sq
where rc_ = rn_ ;
-- output
c1 c2 c3 rowcount
a a a 1
a b a 2
b a b 2
See dbfiddle.
If you are dealing with strings that cannot easily converted to integers, you could create a mapping between the strings and integers, and implement the map_ as a view (so that it is easy to use in subsequent queries) eg
MAP
-- {1} find all distinct elements
-- {2} map each element to an integer
create view map_
as
select val_, rank() over ( order by val_ ) weight_
from (
select distinct val_
from (
select distinct c1 val_ from t union all
select distinct c2 from t union all
select distinct c3 from t
) all_elements
) unique_elements ;
Once you have this map, you can use its values for creating checksums (maybe also in a view) ...
Checksums
create view t_checksums_
as
select c1, c2, c3, c1weight + c2weight + c3weight as checksum
from (
select
c1, ( select weight_ from map_ where c1 = map_.val_ ) c1weight
, c2, ( select weight_ from map_ where c2 = map_.val_ ) c2weight
, c3, ( select weight_ from map_ where c3 = map_.val_ ) c3weight
from t
) valandweight ;
... and then, you can use the same query as before, for obtaining the final result - see dbfiddle.

Case when duplicate add one more letter

For example: I have a table with these records below
1 A
2 A
3 B
4 C
...
and I need to migrate these record in to another table
1 AA
2 AB
3 B
4 C
...
Meaning if the record is duplicate, it will automatically add one more letter alphabetically.
Just a slightly different approach
Example
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',char(64+row_number() over ( partition by SomeCol order by ID ))) )
From #YourTable
Returns
ID SomeCol NewVal
1 A AA
2 A AB
3 B B
4 C C
EDIT - Requested UPDATE
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',replace(char(63+row_number() over ( partition by SomeCol order by ID )),'#','')) )
From #YourTable
Returns
ID SomeCol NewVal
1 A A
2 A AA
3 B B
4 C C
We might be able to handle this requirement with the help of a calendar table mapping secondary letters to duplicate sequence counts:
WITH letters AS (
SELECT 1 AS seq, 'A' AS let UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
...
SELECT 26, 'Z' UNION ALL
...
),
cte AS (
SELECT id, let, ROW_NUMBER() OVER (PARTITION BY let ORDER BY id) rn,
COUNT(*) OVER (PARTITION BY let) cnt
FROM yourTable
)
SELECT t1.id, t1.let + CASE WHEN t1.cnt > 1 THEN t2.let ELSE '' END AS let
FROM cte t1
LEFT JOIN letters t2
ON t1.id = t2.seq
ORDER BY t1.id;
Demo

Unpivot multiple columns not showing desire result

Original
RecordKey Name Section1_Product Section1_Code Section2_Product Section2_Code ......
1 a ff 22
2 b gg 22
3 c hh 33
RecordKey Name Section Product Code ......
1 a 1 ff 22
1 a 2
2 b 1 gg 22
2 b 2
3 c 1 hh 22
3 c 2
I am trying to unpivot the columns into rows. Some sections will have null value.
SELECT RecordKey
,Name
,'Num_of_Sections' = ROW_NUMBER() OVER (PARTITION BY RecordKey ORDER BY ID)
,Product
,Code
FROM (
SELECT RecordKey, Name, Section1_Product, Section1_Code, Section2_Product, Section2_Code FROM Table
) M
UNPITVOT (
Product FOR ID IN (Section1_Product, Section2_Product)
) p
UNPIVOT (
Code FOR CO IN (Section1_Code, Section2_Code)
) c
If I execute with only one column (Product, comment out Code) then I will have 2 values in ID column (1,2). If I run the query with 2 columns then I get 4 values in ID column(1, 2, 3, 4).
may as per my assumption and your data provided we can achieve this using Cross apply and Row_number
declare #Record TABLE
([RecordKey] int,
[Name] varchar(1),
[Section1_Product] varchar(2),
[Section1_Code] int,
[Section2_Product] varchar(2),
[Section2_Code] int)
;
INSERT INTO #Record
([RecordKey], [Name], [Section1_Product], [Section1_Code],[Section2_Product],[Section2_Code])
VALUES
(1, 'a', 'ff', 22,NULL,NULL),
(2, 'b', 'gg', 22,NULL,NULL),
(3, 'c', 'hh', 33,NULL,NULL)
;
With cte as (
Select T.RecordKey,
T.Name,
T.val,
T.val1 from (
select RecordKey,Name,val,val1 from #Record
CROSS APPLY (VALUES
('Section1_Product',Section1_Product),
('Section2_Product',Section2_Product))cs(col,val)
CROSS APPLY (VALUES
('Section1_Code',Section1_Code),
('Section2_Code',Section2_Code))css(col1,val1)
WHERE val is NOT NULL)T
)
Select c.RecordKey,
c.Name,
c.RN,
CASE WHEN RN = 2 THEN NULL ELSE c.val END Product,
c.val1 Code
from (
Select RecordKey,
Name,
ROW_NUMBER()OVER(PARTITION BY val ORDER BY (SELECT NULL))RN,
val,
val1 from cte )C

SQL Server Query- How to Prune a table

I have a Table 'L2'
Itemset Itemset2
1 3
2 3
2 5
3 5
I created a [combination in pair of three] for these values in columns in table 'c3'
Itemset Itemset2 itemset3
1 3 5
2 3 5
1 2 3
1 2 5
Like in Apriori I want to prune the table C3.
i.e. Getting this table as output 'C3Prune'
Itemset Itemset2 itemset3
2 3 5
I want to create a SQL Server query for the same, I tried loops but it's not correct.
2 ways are provided in this answer, I want to give another query with using of UNION and EXCEPT:
select *
from C3
where not exists
(
select c3.itemset a, c3.itemset2 b
union
select c3.itemset a, c3.itemset3 b
union
select c3.itemset2 a, c3.itemset3 b
except
select itemset a, itemset2 b from l2
)
Output:
2 3 5
SQLFIDDLE DEMO
Try this:
DECLARE #L2 TABLE ( I1 INT, I2 INT )
DECLARE #C3 TABLE ( I1 INT, I2 INT, I3 INT )
INSERT INTO #L2
VALUES ( 1, 3 ),
( 2, 3 ),
( 2, 5 ),
( 3, 5 )
INSERT INTO #C3
VALUES ( 1, 3, 5 ),
( 2, 3, 5 ),
( 1, 2, 3 ),
( 1, 2, 5 )
--Version 1
SELECT c.*
FROM #C3 c
JOIN #L2 l1 ON c.I1 = l1.I1 AND c.I2 = l1.I2
JOIN #L2 l2 ON c.I1 = l2.I1 AND c.I3 = l2.I2
JOIN #L2 l3 ON c.I2 = l3.I1 AND c.I3 = l3.I2
--Version 2
SELECT * FROM #C3 c
WHERE
EXISTS(SELECT * FROM #L2 WHERE I1 = c.I1 AND I2 = c.I2) AND
EXISTS(SELECT * FROM #L2 WHERE I1 = c.I1 AND I2 = c.I3) AND
EXISTS(SELECT * FROM #L2 WHERE I1 = c.I2 AND I2 = c.I3)
Output:
I1 I2 I3
2 3 5

Select first row in each GROUP BY group

I have a requirement in my project that I have this data with me:
C1 | C2 | C3 | C4
A | B | 2 | X
A | B | 3 | Y
C | D | 4 | Q
C | D | 1 | P
Where C1, C2, C3 and C4 are columns name in Database
And I have need to show data like this
C1 | C2 | C3 | C4
A | B | 5 | X
C | D | 5 | Q
The answer to this is fairly simple. Just follow my solution below:
--CREATE THE SAMPLE TABLE
CREATE TABLE TABLE1 (C1 char(1) NULL, C2 char(1) NULL, C3 int NULL, C4 char(1) NULL);
GO
--INSERT THE SAMPLE VALUES
INSERT INTO TABLE1 VALUES ('A', 'B', 2, 'X'), ('A', 'B', 3, 'Y'), ('C', 'D', 4, 'Q'), ('C','D', 1, 'P');
GO
--SELECT SUM(C3) AND GROUP BY ONLY C1 AND C2, THEN SELECT TOP 1 ONLY FROM C4
SELECT
C1,
C2,
SUM(C3) AS C3,
(SELECT TOP(1) C4 FROM TABLE1 AS B WHERE A.C1 = B.C1) AS C4
FROM
TABLE1 AS A
GROUP BY
C1,
C2;
GO
--CLEAN UP THE DATABASE, DROP THE SAMPLE TABLE
IF EXISTS(SELECT name FROM sys.tables WHERE object_id = OBJECT_ID(N'TABLE1')) DROP TABLE TABLE1;
GO
Let me know if this helps.
Assuming you mean the first record ordered by c4 (grouped by c1 and c2), then this will work establishing a row_number and using max with case:
with cte as (
select *,
row_number() over (partition by c1, c2 order by c4) rn
from yourtable
)
select c1, c2, sum(c3), max(case when rn = 1 then c4 end) c4
from cte
group by c1, c2
SQL Fiddle Demo
However, if you don't want to order by c4, then you need some other column to ensure the correct order of the results. Without an order by clause, there's no guarantee on how they are returned.
I hope you choose 'X' and 'Q' as those rows where inserted first, while grouping C1 and C2.
I would suggest you to add an identity column in your table and work based on it as given below.
Table:
DECLARE #DB TABLE (ID INT IDENTITY(1,1),C1 VARCHAR(10),C2 VARCHAR(10),C3 INT,C4 VARCHAR(10))
INSERT INTO #DB VALUES
('A','B',2,'X'),
('A','B',3,'Y'),
('C','D',4,'Q'),
('C','D',1,'P')
Code:
SELECT A.*,B.C4
FROM (
SELECT C1,C2,SUM(C3) C3 FROM #DB
GROUP BY C1,C2) A
JOIN
(
SELECT C1,C2,C4 FROM (
SELECT *,ROW_NUMBER() OVER (PARTITION BY C1,C2 ORDER BY ID) [ROW]
FROM #DB) LU WHERE LU.ROW = 1) B
ON A.C1 = B.C1 AND A.C2 = B.C2
Result: