How to get last child id using SQL Server - sql

I have a table with 2 columns MainID and ChildId.
My table:
MainID ChildId
-------------------------------
1 1
1 2
1 3
2 1
2 2
3 1
4 1
4 2
5 1
5 2
5 3
5 4
I want to select only last child Id for each MainId:
MainID ChildId
------------------------------
1 3
2 2
3 1
4 2
5 4
For this I am trying to use Top (1), but its only returning one row:
SELECT TOP (1)
MainId, ChildId
FROM
MYTABLE
ORDER BY
ChildId DESC

You need top 1 with ties
SELECT TOP (1) with ties MainId, ChildId
FROM MYTABLE
ORDER BY row_number() over (partition by MainId order by ChildId desc)
Also in that case you can not just order by ChildId because all values in top must be same. So you need row_number

Try this:
select MainId,
MAX(ChildId)
from MY_TABLE
group by MainId

If the last child is the one with the largest id, you can do:
SELECT TOP (1) WITH TIES MainId, ChildId
FROM MYTABLE
ORDER BY ROW_NUMBER() OVER (PARTITION BY MainId ORDER BY ChildId DESC);
There are other ways to accomplish this with subqueries -- and using window functions in ORDER BY is not initially intuitive.
I should note that this is SQL-Server-specific, but not because of ROW_NUMBER() in the ORDER BY clause. SQL Server supports TOP (n) WITH TIES, and there is not corresponding functionality in other databases.

Related

How to produce a circular shift with postgreSQL

Let's suppose that I have this simple table:
ID
1
2
3
4
If I do:
WITH example AS
(
SELECT UNNEST(ARRAY[1,2,3,4]) as ID
)
SELECT ID, lAG(ID,1) over() as LAG_ID
FROM example
-- The shift is 1 in this case, but could be any integer in practice.
I got:
ID
LAG_ID
1
2
1
3
2
4
3
But I need a circular shift:
ID
LAG_ID
1
4
2
1
3
2
4
3
Is there an elegant way to produce this result ?
If ID is not nullable
WITH example AS
(
SELECT UNNEST(ARRAY[1,2,3,4]) as ID
)
SELECT ID, coalesce(lAG(ID,1) over(order by ID asc), (select max(ID) from example)) as LAG_ID
FROM example

How to find the most frequently repeated column?

ID UserID LevelID
1 1 1
2 1 2
3 1 2
4 1 2
5 2 1
6 2 3
7 3 2
8 4 1
9 4 1
The query should return: LevelID: 1 (3 times) - the LevelID column that is most frequently repeated by different Users (UserID).
I have the following query:
SELECT LevelID, COUNT(LevelID) AS 'Occurrence'
FROM
(
SELECT DISTINCT * FROM
(
SELECT UserID, LevelID
FROM SampleTable
) cv
) levels
GROUP BY LevelID
ORDER BY 'Occurrence' DESC
Which returns:
LevelID Occurence
1 3
2 2
3 1
But it doesn't let me to add LIMIT 1; at the bottom to retrieve the first top row of the selection. What's wrong with the query?
There is no need for these several levels of nesting. Consider using aggregation, count(distinct ...), ordering the results and using a row-limiting clause to keep the top record only:
select top(1) levelID, count(distinct userID) cnt
from mytable
group by levelID
order by cnt desc
If you want to allow possible top ties, then use top (1) with ties instead of just top (1).

sql - select single ID for each group with the lowest value

Consider the following table:
ID GroupId Rank
1 1 1
2 1 2
3 1 1
4 2 10
5 2 1
6 3 1
7 4 5
I need an sql (for MS-SQL) select query selecting a single Id for each group with the lowest rank. Each group needs to only return a single ID, even if there are two with the same rank (as 1 and 2 do in the above table). I've tried to select the min value, but the requirement that only one be returned, and the value to be returned is the ID column, is throwing me.
Does anyone know how to do this?
Use row_number():
select t.*
from (select t.*,
row_number() over (partition by groupid order by rank) as seqnum
from t
) t
where seqnum = 1;

Is there a way to update groups of rows with separate incrementing values in one query

Lets say you have the following table:
Id Index
1 3
1 1
2 1
3 3
1 5
what I would like to have is the following:
Id Index
1 0
1 1
2 0
3 0
1 2
As you might notice, the goal is for every row where Id is the same, to incrementally update the Index column, starting from zero.
Now, I know this is fairly simple with using cursors, but out of curiosity is there a way to do this with single UPDATE query, somehow combining with temp tables, common table expressions or something similar?
Yes, assuming that the you don't really care about the order of the values for the new index values. SQL Server offers updatable CTEs and window functions that do exactly what you want:
with toupdate as (
select t.*, row_number() over (partition by id order by (select NULL)) as newindex
from table t
)
update toupdate
set index = newindex;
If you want them in a specific order, then you need another column to specify the ordering. The existing index column doesn't work.
With Row_number() -1 and CTE you can write as:
CREATE TABLE #temp1(
Id int,
[Index] int)
INSERT INTO #temp1 VALUES (1,3),(1,1),(2,1),(3,3),(1,5);
--select * from #temp1;
With CTE as
(
select t.*, row_number() over (partition by id order by (select null))-1 as newindex
from #temp1 t
)
Update CTE
set [Index] = newindex;
select * from #temp1;
Demo
I'm not sure why you would want to do this really, but I had fun figuring it out!
This solution relies on your table having a primary key for the self join... but you could always create an auto inc index if none exists and this is a one off job... This will also have the added benefit of getting you to think about the precise ordering of this you want... as currently there is no way of saying which order [ID] will get [Index] in.
UPDATE dbo.Example
SET [Index] = b.newIndex
FROM dbo.Example a
INNER JOIN (
select
z.ID,
z.[Index],
(row_number() over (partition by ID order by (select NULL))) as newIndex
from Example z
) b ON a.ID = b.ID AND a.[Index]=b.[Index] --Is this a unique self join for your table?.. no PK provided. You might need to make an index first.
Probably, this is what you want
SELECT *,RANK() OVER(PARTITION BY Id ORDER BY [Index])-1 AS NewIndex FROM
(
SELECT 1 AS Id,3 [Index]
UNION
SELECT 1,1
UNION
SELECT 2,1
UNION
SELECT 3,3
UNION
SELECT 1,5
) AS T
& the result will come as
Now if you want to update the table then execute this script
UPDATE tblname SET Index=RANK() OVER(PARTITION BY t.Id ORDER BY t.[Index])-1
FROM tblname AS t
In case I am missing something or any further assistance is required please let me know.
CREATE TABLE #temp1(
Id int,
Value int)
INSERT INTO #temp1 VALUES (1,2),(1,3),(2,3),(4,5)
SELECT
Id
,Value
,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) Id
FROM #temp1
Start with this :)
Gave me results like
Id Value Count
1 2 1
1 3 2
1 2 3
1 3 4
1 2 5
1 3 6
1 2 7
1 3 8
2 3 1
2 4 2
2 5 3
2 3 4
2 4 5
2 5 6
2 4 7
2 5 8
2 3 9
2 3 10
3 4 1
4 5 1
4 5 2
4 5 3
4 5 4

Applying a sort order to existing data using SQL 2008R2

I have some existing data that I need to apply a "SortOrder" to based upon a few factors:
The ordering starts at "1" for any given Owner
The ordering is applied alphabetically (basically following an ORDER BY Name) to increase the sort order.
Should two items have the same name (as I've illustrated in my data set), we can apply the lower sort order value to the item with the lower id.
Here is some sample data to help illustrate what I'm talking about:
What I have:
Id OwnerId Name SortOrder
------ ------- ---------------------- ---------
1 1 A Name NULL
2 1 C Name NULL
3 1 B Name NULL
4 2 Z Name NULL
5 2 Z Name NULL
6 2 A Name NULL
What I need:
Id OwnerId Name SortOrder
------ ------- ---------------------- ---------
1 1 A Name 1
3 1 B Name 2
2 1 C Name 3
6 2 A Name 1
4 2 Z Name 2
5 2 Z Name 3
This could either be done in the form of an UPDATE statement or doing an INSERT INTO (...) SELECT FROM (...) if it's easier to move the data from one table to the next.
Easy - use a CTE (Common Table Expression) and the ROW_NUMBER() ranking function:
;WITH OrderedData AS
(
SELECT Id, OwnerId, Name,
ROW_NUMBER() OVER(PARTITION BY OwnerId ORDER BY Name, Id) AS 'SortOrder'
FROM
dbo.YourTable
)
SELECT *
FROM OrderedData
ORDER BY OwnerId, SortOrder
The PARTITION BY clause groups your data into group for each value of OwnerId and the ROW_NUMBER() then starts counting at 1 for each new group of data.
Update: If you want to update your table to set the SortOrder column - try this:
;WITH OrderedData AS
(
SELECT
Id, OwnerId, Name,
ROW_NUMBER() OVER(PARTITION BY OwnerId ORDER BY Name, Id) AS 'RowNum'
FROM
dbo.YourTable
)
UPDATE OrderedData
SET SortOrder = RowNum
That should set the SortOrder column to the values that the ROW_NUMBER() function returns