t-sql single column result-set to 3 columns - sql-server

This is a bit of a weird question, and I know it would probably be easier to not do it in SQL, but it will make my life a lot easier.
Basically I have a single column result-set, and I need to turn that into 3 columns, not based on any criteria.
eg.
1
2
3
4
5
6
7
into:
1 2 3
4 5 6
7
It will always be a fixed 3 column result I need in this case.
Currently I am using a cursor and inserting into a table variable, which seems a bit terrible. There must be a better way.
Thanks

Try this:
declare #t table(n int)
insert #t(n) values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
select [0],[1],[2]
from
(
select n
, (ROW_NUMBER() over (order by n) - 1) % 3 c
, (ROW_NUMBER() over (order by n) - 1) / 3 r
from #t
) x
pivot (max(n) for c in ([0], [1], [2])) p

It's possible, but man is this an ugly requirement. This really belongs in the presentation tier, not in the sql.
WITH original As
(
SELEZCT MyColumn, row_number() over (order by MyColumn) as ordinal
FROM RestOfOriginalQueryHere
),
Grouped As
(
SELECT MyColumn, ordinal / 3 As row, ordinal % 3 As col
FROM original
)
SELECT o1.MyColumn, o2.MyColumn, o3.MyColumn
FROM grouped g1
LEFT JOIN grouped g2 on g2.row = g1.row and g2.col = 1
LEFT JOIN grouped g3 on g2.row = g1.row and g3.col = 2
WHERE g1.col = 0

Related

SQL Server update rows without nulls ordered by consecutive numbers and nulls

I have query returning few rows. There is column with consecutive numbers and nulls in it.
For example, it has values from 1-10 then 5 nulls, then from 16-30 and then 10 nulls, then from 41-45 and so on.
I need to update that column or create another column to create groupId for consecutive columns.
Meaning as per above example, for rows 1-10, groupID can be 1. Then for 5 nulls nothing and then from 16-30 groupId can be 2. Then for 10 nulls nothing. Then from 41-45 groupId can be 3 and so on.
Please let me know
This was a fun one. Here is the solution with a simple table that contains just integers, but with gaps.
create table n(v int)
insert n values (1),(2),(3),(5),(6),(7),(9),(10)
select n.*, g.group_no
from n
join (
select row_number() over (order by low.v) group_no, low.v as low, min(high.v) as high
from n as low
join n as high on high.v>low.v
and not exists(select * from n h2 where h2.v=high.v+1)
where not exists(select * from n l2 where l2.v=low.v-1)
group by low.v
) g on g.low<=n.v and g.high>=n.v
Result:
v group_no
1 1
2 1
3 1
5 2
6 2
7 2
9 3
10 3
Typical island & gap solution
select col, grp = dense_rank() over (order by grp)
from
(
select col, grp = col - dense_rank() over (order by col)
from yourtable
) d

Get X and Y coordinates of each cell from a table and transform into a new table in sql server

I'm trying to build a query for a matrix table which has a schema like this:
ID 1 2 3
----------- ----------- ----------- -----------
1 13 32 55
2 30 75 129
I want to get the position of a cell according to its coordinate (row number and column number) to create a new table that has the fields row_num, col_num and value
In the example given, this query should return:
row_num col_num value
------- ------- -----------
1 1 13
2 1 30
1 2 32
2 2 75
1 3 55
2 3 129
The query must obtain the value of each cell and return its position X and Y.
I have tried different approach without success. I tried to use UNPIVOT, but it is not showing me the correct information.
Any suggestions are greatly appreciated!
UPDATED:
I added a column whit row number
You need to unpivot the data and generate row number. Here is one way using CROSS APPLY
select Row_number()over(partition by col_num order by ID) as row_num,
col_num,
value
from yourtable
cross apply(values ([1],'1'),([2],'2'),([3],'3')) tc (value,col_num)
To do this using unpivot try this way
select Id,col_num,value
from Yourtable
unpivot
(
value
for col_num in ([1], [2], [3])
) u;
Assuming that you do have a column that specifies the ordering, you can do the calculation as:
select dense_rank() over (order by ??) as row_num,
v.col_num, v.val
from matrix m cross apply
(values (m.col1, 1), (m.col2, 2), (m.col3, 3)
) v(val, col_num);
SQL tables represent unordered sets. The ?? is for whatever column specifies the ordering. If it is already row_num, then you don't need the dense_rank().
For the Updated Question
Declare #YourTable table (ID int,[1] int,[2] int,[3] int)
Insert Into #YourTable values
(1,13,32,55),
(2,30,75,129)
Select A.ID as row_nu,
,B.*
From #YourTable A
Cross Apply (
values (1,A.[1])
,(2,A.[2])
,(3,A.[3])
) B (col_num,value)
Order by B.col_num,A.ID
Returns
row_num col_num value
1 1 13
2 1 30
1 2 32
2 2 75
1 3 55
2 3 129
EDIT - As requested UnPivot
Select ID as row_num ,col_num,value
From #Yourtable
UnPivot (Value for col_num in ([1], [2], [3]) ) B
Order By 2,1

Using t-sql to select aggregate when date difference is not just equal but small

I have a table where I want to select the maximum of a column but based on when the date difference is equal or small (lets say 3 days). When two subsequent dates are very close, the data are likely spurious and I want to get the highest state when that happens.
My data looks similar to this
DECLARE #TestingResults TABLE (
IDNumber varchar(100),
DateSeen date,
[state] int)
INSERT INTO #TestingResults VALUES
('A','2015-04-21',2),
('A','2015-05-08',2),
('A','2015-07-01',3),
('B','2014-06-18',100), -- this is the one I want
('B','2014-06-19',2),
('B','2014-07-31',2),
('B','2014-08-11',3),
('B','2014-09-24',3),
('B','2014-10-24',3),
('B','2014-11-24',3),
('B','2014-12-15',3),
('B','2015-01-12',3),
('B','2015-01-13',400), -- this is the one I want
('B','2015-04-06',10), -- either will do
('B','2015-04-07',10),
('B','2015-07-06',3), -- either will do
('B','2015-07-07',3),
('B','2015-10-12',3),
('C','2012-02-20',3),
('C','2012-03-12',3),
('C','2012-04-02',3),
('C','2012-11-21',3)
What I really want is something like this where I take the maximum of state when the difference between dates is < 3 (note, some of the data may have the same state even when the differences in date are small ...) :
IDNumber DateSeen state
A 2015-04-21 2
A 2015-05-08 2
A 2015-07-01 3
-- if there are observations < 3 days apart, take MAX
B 2014-06-18 100
B 2014-07-31 2
B 2014-08-11 3
B 2014-09-24 3
B 2014-10-24 3
B 2014-11-24 3
B 2014-12-15 3
-- if there are observations < 3 days apart, take MAX
B 2015-01-13 400
-- if there are observations < 3 days apart, take MAX
B 2015-04-07 10
-- if there are observations < 3 days apart, take MAX
B 2015-07-07 3
B 2015-10-12 3
C 2012-02-20 3
C 2012-03-12 3
C 2012-04-02 3
C 2012-11-21 3
I guess I could create another variable table to hold it and then query it but there are a couple of problems. First as you can see, IDNumber='B' has a couple of triggers in its sequences of dates so I am thinking there should be an 'smarter' way.
Thanks!
After your clarifying comments (thanks for that!), I would do this as follows:
SELECT ISNULL(high.IDNumber, results.IDNumber) AS IDNumber,
ISNULL(high.DateSeen, results.DateSeen) AS DateSeen,
ISNULL(high.[state], results.[state]) AS [state]
FROM #TestingResults results
OUTER APPLY
(
SELECT TOP 1 IDNumber, DateSeen, [state]
FROM #TestingResults highest
WHERE highest.DateSeen < results.DateSeen
AND highest.IDNumber = results.IDNumber
AND DATEDIFF(DAY,highest.DateSeen,results.DateSeen) <=3
ORDER BY [state] DESC, [DateSeen] DESC
) high
WHERE NOT EXISTS
(
SELECT 1
FROM #TestingResults nearFuture
WHERE nearFuture.DateSeen > results.DateSeen
AND nearFuture.IDNumber = results.IDNumber
AND DATEDIFF(DAY,results.DateSeen,nearFuture.DateSeen) <=3
)
This is almost certainly not the most elegant way to achieve this (I suspect this could be done more efficiently with Window Functions or a recursive CTE or similar), I believe it gives you the behaviour and results you desire.
This should do it using a recursive CTE:
WITH TestingResults AS (
SELECT
*
,ROW_NUMBER() OVER(ORDER BY IDNumber, DateSeen) AS RowNum
FROM #TestingResults
), Data AS (
SELECT
tmp1.IDNumber,
tmp1.DateSeen,
tmp1.state,
tmp1.RowNum,
tmp1.RowNum AS GroupID
FROM (
SELECT
*
,ABS(DATEDIFF(DAY, DateSeen, LAG(DateSeen, 1, NULL) OVER(PARTITION BY IDNumber ORDER BY DateSeen))) AS AbsPrev
FROM TestingResults
) AS tmp1
WHERE tmp1.AbsPrev IS NULL OR tmp1.AbsPrev >= 3 --the first date in a sequence
UNION ALL
SELECT
r.IDNumber,
r.DateSeen,
r.state,
r.RowNum,
d.GroupID
FROM Data d
INNER JOIN TestingResults r ON
r.IDNumber = d.IDNumber
AND DATEDIFF(DAY, d.DateSeen, r.DateSeen) < 3
AND d.RowNum+1 = r.RowNum
)
SELECT MIN(d.IDNumber) AS IDNumber, MAX(d.DateSeen) AS DateSeen, MAX(d.state) AS state
FROM Data d
GROUP BY d.GroupID

Select randomly few Rows of the same ID in the same table (T-SQL)

I'm trying to select randomly few rows for each Id stored in one table where these Ids have multiple rows on this table. It's difficult to explain with words, so let me show you with an example :
Example from the table :
Id Review
1 Text11
1 Text12
1 Text13
2 Text21
3 Text31
3 Text32
4 Text41
5 Text51
6 Text61
6 Text62
6 Text63
Result expected :
Id Review
1 Text11
1 Text13
2 Text21
3 Text32
4 Text41
5 Text51
6 Text62
In fact, the table contains thousands of rows. Some Ids contain only one Review but others can contain hundreds of reviews. I would like to select 10% of these, and select at least once, all rows wich have 1-9 reviews (I saw the SELECT TOP 10 percent FROM table ORDER BY NEWID() includes the row even if it's alone)
I read some Stack topics, I think I have to use a subquery but I don't find the correct solution.
Thanks by advance.
Regards.
Try this:
DECLARE #t table(Id int, Review char(6))
INSERT #t values
(1,'Text11'),
(1,'Text12'),
(1,'Text13'),
(2,'Text21'),
(3,'Text31'),
(3,'Text32'),
(4,'Text41'),
(5,'Text51'),
(6,'Text61'),
(6,'Text62'),
(6,'Text63')
;WITH CTE AS
(
SELECT
id, Review,
row_number() over (partition by id order by newid()) rn,
count(*) over (partition by id) cnt
FROM #t
)
SELECT id, Review
FROM CTE
WHERE rn <= (cnt / 10) + 1
Result(random):
id Review
1 Text12
2 Text21
3 Text31
4 Text41
5 Text51
6 Text63

Using a While Loop to update a field by 1 each time a value changes

So I have a table that has two records that need to be one. I can identify them but I want to update them in groups (sort of like a scan update =1, then proceed, then some other field changes, increment the number by 1 and proceed.)
Example table:
IDEvent 1 2 3 4 5
Col1 1 1 0 1 0
Col2 a a b a b
So essentially, my outcome would look like this afterwards so that I can write a select and group by col1 to then group the two first records into one but leave non consecutive record alone. I tried while loops but I couldn't figure it out.
IDEvent 1 2 3 4 5
Col1 1 1 0 2 0
Col2 A A B A B
alter view PtypeGroup as
WITH q AS
(
SELECT *,
ROW_Number() OVER (PARTITION BY idsession, comment ORDER BY ideventrecord) AS rnd,
ROW_NUMBER() OVER (PARTITION BY idsession ORDER BY ideventrecord) AS rn
FROM [ratedeventssorted]
)
SELECT min(ideventrecord) as IDEventRecord, idsession, min(distancestamp) as distancestamp, sum(length) as length, min(comment) as comment2, min(eventscorename) as firstptype, min(eventscoredescription) as Ptype2,
MIN(ideventrecord) AS first_number,
MAX(ideventrecord) AS last_number,
comment
,COUNT(ideventrecord) AS numbers_count
--into test
FROM q
where eventscorename IN ('Flex', 'Chpsl')
GROUP BY idsession,
rnd - rn,
comment

Resources