How to create "subsets" as a result from a column in SQL - sql

Let's suppose that I've got as a result from one query the next set of values of one column:
Value
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Now, I would like to see this information with another order, establishing a limit to the number of values of every single subset. Now suppose that I choose 3 as a limit,the information will be given like this (one column for all the subsets):
Values
1 A, B, C
2 D, E, F
3 G, H, I
4 J,
Obviously, the last row will contain the remaining values when their number is smaller than the limit established.
Is it possible to perform a query like this in SQL?
What about if the limit is dynamic?. It can be chosen randomly.

create table dee_t (id int identity(1,1),name varchar(10))
insert into dee_t values ('A'),('B'),('c'),('D'),('E'),('F'),('g'),('H'),('I'),('J')
;with cte as
(
select (id-1)/3 +1 rno ,* from dee_t
) select rno ,
(select name+',' from cte where rno = c.rno for xml path('') )
from cte c group by rno

You can do this by using few calculations with row_number, like this:
select
GRP,
max(case when RN = 1 then Value end),
max(case when RN = 2 then Value end),
max(case when RN = 0 then Value end)
from (
select
row_number() over (order by Value) % 3 as RN,
(row_number() over (order by Value)+2) / 3 as GRP,
Value
from Table1
) X
group by GRP
The first row_number creates numbers for the columns (1,2,0,1,2,0...) and the second one creates numbers for the rows (1,1,1,2...). Those are then used to group the values into correct place using case, but you can also use pivot instead of it if you like it more.
If you want them into same column, of course just concatenate the cases instead of selecting them on different columns, but beware of nulls.
Example in SQL Fiddle

Thanks a lot for all your reply. Finally I've got a Solution with the help of Rajen Singh
This is the code than can be used:
WITH CTE_0 AS
(
SELECT DISTINCT column_A_VALUE AS id
FROM Table
WHERE column_A_VALUE IS NOT NULL
), CTE_1 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY id) RN, id
FROM CTE_0
), CTE_2 AS
(
SELECT RN%30 GROUP, ID
FROM CTE_1
)SELECT STUFF(( SELECT ','''+CAST(ID AS NVARCHAR(20))+''''
FROM CTE_2
WHERE GROUP = A.GROUP
FOR XML PATH('')),1,1,'') IDS
FROM CTE_2 A
GROUP BY GROUP

Related

Frequency of all combinations of values for certain column

I have a dataset in SQL Server 2012 with a column for id and value, like this:
[id] [value]
--------------
A 15
A 11
A 11
B 13
B 15
B 12
C 12
C 13
D 13
D 12
My goal is to get a frequency count of all combinations of [value], with two caveats:
Order doesn't matter, so [11,12,15] is not counted separately from [12,11,15]
Repeated values are counted separately, so [11,11,12,15] is counted separately from [11,12,15]
I'm interested in all combinations, of any length (not just pairs)
So the outcome would look like:
[combo] [frequency]
---------------------
11,11,15 1
12,13,15 1
12,13 2
I've seen answers here involving recursion that answer similar questions but where order counts, and answers here involving self-joins that yield pair-wise combinations. These come close but I'm not quite sure how to adapt for my specific needs.
You can use string_agg():
select vals, count(*) as frequency
from (select string_agg(value, ',') within group (order by value) as vals, id
from t
group by id
) i
group by vals;
SQL Server 2012 doesn't support string_agg() but you can use the XML hack:
select vals, count(*) as frequency
from (select id,
stuff( (select concat(',', value)
from t t2
where t2.id = i.id
for xml path ('')
), 1, 1, ''
) as vals
from (select distinct id from t) i
) i
group by vals;
Your number string is just all the values with the same id in increasing order. So I'm treating the lowest id as a canonical name for the full sequence and all its matches. This spares all the string manipulations though you can expand as necessary.
Just tag each duplicate value with a counter and then look for groups that pair up completely.
with data as (
select id, value,
row_number() over (partition by id, value) as rn
), matches as (
select l.id, r.id as match
from data l full outer join data r on
l.value = r.value and l.rn = r.rn and l.id <= r.id
group by l.id
having count(l.id) = count(*) and count(r.id) = count(*)
)
select id, count(match) as frequency
from matches
group by id;
The logic in the middle query is also easily adaptable for finding subset of values in common.
You can achieve this using CTEs and row_number functions.
DECLARE #table table(id CHAR(1), val int)
insert into #table VALUES
('A',15),
('A',11),
('A',11),
('B',13),
('B',15),
('B',12),
('C',12),
('C',13),
('D',13),
('D',12);
;WITH CTE_rnk as
(
SELECT id,val, row_number() over (partition by id order by val) as rnk
from #table
),
CTE_concat as
(
SELECT id, cast(val as varchar(100)) as val, rnk
from CTE_rnk
where rnk =1
union all
SELECT r.id, cast(concat(c.val,',',r.val) as varchar(100)) as val,r.rnk
from CTE_rnk as r
inner join CTE_concat as c
on r.rnk = c.rnk+1
and r.id = c.id
),
CTE_maxcombo as
(
SELECT id,val, row_number() over(partition by id order by rnk desc) as rnk
from CTE_concat
)
select val as combo, count(*) as frequency
from CTE_maxcombo where rnk = 1
group by val
+----------+-----------+
| combo | frequency |
+----------+-----------+
| 11,11,15 | 1 |
| 12,13 | 2 |
| 12,13,15 | 1 |
+----------+-----------+

SQL Server Row_number in ORDER BY CASE

EDITED THE WHOLE TOPIC.
I need to create a view that sort article per type.
If I only have the type : *VALUE -> I need to show this line only.
If I have the type : *VALUE & 2 -> Still showing row accordingly to *VALUE type only.
If I only have the type : 2 -> Showing this one.
I already did somethink like this :
VALUE* is a value that should come from an another table with a Join.
SELECT Id_item ,Name_item , Type_item , Id_type_item FROM ITEM
WHERE Name_item = 'Gillette' AND (Id_Type_item = VALUE* OR Id_Type_item ='10')
ORDER BY CASE
WHEN row_number() OVER(ORDER BY Id_item DESC , Id_Type_Item DESC) <= 1 THEN 0
ELSE 1
END;
But it does that in the case where we've got both row for the types(*VALUE & 10):
Id_item / Name_item / Type_item / Id_Type_Item
1 Gillette 45 30 (*VALUE)
1 Gillette 2 10
So I think that the order by on the Over() could be useful to always sort by *VALUE (which are in reality another column from another table)
I always want to select 1 row of data only ! :)
I'm guessing, that what you want is the "first" row returned from each SELECT? There's no need to use a separate SELECT statement for each variable on the same table, you can use a window function to do so. I believe this is what you might be after.
WITH CTE AS(
--The following assumes table A and B have the same DDL (which begs the question, why are they different tables?)
SELECT *,
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM A
WHERE var IN (1,2)
UNION --ALL(?)
SELECT *
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM B
WHERE var IN (3))
SELECT *
FROM CTE
WHERE RN = 1;
Here is a possible solution. In this case ROW_NUMBER, RANK and DENSE_RANK would all work. However, ROW_COUNT is not a valid window function in sql server.
DECLARE #A TABLE(ID INT, Value INT)
DECLARE #B TABLE(ID INT,Value INT)
INSERT INTO #A VALUES (1,1),(2,1),(3,2),(4,3),(5,2),(6,1),(7,3)
INSERT INTO #B VALUES (1,1),(2,1),(3,1),(4,2),(5,3),(6,2),(7,1),(8,3)
;WITH D AS
(
SELECT ID,Value FROM #A WHERE Value IN(1,2)
UNION ALL
SELECT ID,Value FROM #B WHERE Value IN (3)
)
SELECT * FROM
(
SELECT
ID, Value,
ValueRankInSet = DENSE_RANK() OVER(PARTITION BY VALUE ORDER BY ID) -- <-- If you do not have an ID field you can subst ID with NEWID() as order is not important
FROM D
)AS X
WHERE ValueRankInSet = 1
Assign the priority within your Select(s) and then order by it in the row_number:
with cte as
(
SELECT *,
row_number()
over (-- partition by ???
order by prio) as Position
FROM
(
SELECT 1 as prio, * FROM A WHERE var = 1
UNION -- probably a more efficient UNION ALL
SELECT 2 as prio, * FROM A WHERE var = 2
UNION -- probably a more efficient UNION ALL
SELECT 3 as prio, * FROM B WHERE var = 3
)
)
select *
from cte
WHERE Position = 1

get intervals of nonchanging value from a sequence of numbers

I need to sumarize a sequence of values into intervals of nonchanging values - begin, end and value for each such interval. I can easily do it in plsql but would like a pure sql solution for both performance and educational reasons. I have been trying for some time to solve it with analytical functions, but can't figure how to properly define windowing clause. The problem I am having is with a repeated value.
Simplified example -
given input:
id value
1 1
2 1
3 2
4 2
5 1
I'd like to get output
from to val
1 2 1
3 4 2
5 5 1
You want to identify groups of adjacent values. One method is to use lag() to find the beginning of the sequence, then a cumulative sum to identify the groups.
Another method is the difference of row number:
select value, min(id) as from_id, max(id) as to_id
from (select t.*,
(row_number() over (order by id) -
row_number() over (partition by val order by id
) as grp
from table t
) t
group by grp, value;
Using a CTE to collect all the rows and identifying them into changing values, then finally grouping together for the changing values.
CREATE TABLE #temp (
ID INT NOT NULL IDENTITY(1,1),
[Value] INT NOT NULL
)
GO
INSERT INTO #temp ([Value])
SELECT 1 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 1;
WITH Marked AS (
SELECT
*,
grp = ROW_NUMBER() OVER (ORDER BY ID)
- ROW_NUMBER() OVER (PARTITION BY Value ORDER BY ID)
FROM #temp
)
SELECT MIN(ID) AS [From], MAX(ID) AS [To], [VALUE]
FROM Marked
GROUP BY grp, Value
ORDER BY MIN(ID)
DROP TABLE #temp;

Select all columns from table group by column

'test_table' contains a column called vendor, with char values from 1 to 9, 1 being highest priority. 'test_table' also has another column named match, with char values either 'I' or 'H', with 'I' receiving higher priority. I want to return all rows with unique values in ID, prioritized by match, then vendor.
Test_Table
ID Vendor Match
1 3 I
1 2 I
1 4 H
2 1 H
2 1 I
3 1 H
3 2 I
Results Desired
ID Vendor Match
1 2 I
2 1 I
3 2 I
SELECT *
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS RowNo, *
FROM test_table) x
WHERE RowNo = 1
ORDER BY ID
Any help would be greatly appreciated. Thank you!
You need to have an order by clause for match column ( character comparison happens based on ascii value) and another one on vendor column . You can even remove ascii keyowrd ( for the sake of clarity i have written) and simply use the column name in order by clause
Try this :
;With cte as
(
Select ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ascii(match) desc ,vendor )
AS RowNo, Id,Vendor ,Match
FROM test_table
)
Select Id,Vendor,Match from cte
WHERE RowNo = 1
SELECT Id,Vendor,Match
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID,Match Desc,Vendor Asc) AS RowNo, *
FROM test_table ) x
WHERE RowNo = 1
ORDER BY ID

get subset of a table in SQL

I want to get a subset of a table, here's the example:
1 A
2 A
3 B
4 B
5 C
6 D
7 D
8 D
I want to get the unique record, but with the smallest id:
1 A
3 B
5 C
6 D
How can I write the SQL in SQL Server? Thanks!
Use a common-table expression like this:
;WITH DataCTE AS
(
SELECT ID, OtherCol,
ROW_NUM() OVER(PARTITION BY OtherCol ORDER BY ID) 'RowNum'
FROM dbo.YourTable
)
SELECT *
FROM DataCTE
WHERE RowNum = 1
This "partitions" your data by the second column you have (A, B, C) and orders by the ID (1, 2, 3) - smallest ID first.
Therefore, for each "partition" (i.e. each value of your second column), the entry with RowNum = 1 is the one with the smallest ID for each value of the second column.
select min(id), othercol
from thetable
group by othercol
and maybe with
order by othercol
... at the end if thats important
Try this:
SELECT MIN(Id) AS Id, Name
FROM MyTable
GROUP BY Name
select min(id), column2
from table
group by column2
It helps if you provide the table information in the question - I've just guessed at the column names...