T-SQL identify first time value appears in column - sql-server

I am working in Excel 2013 and connecting to SQL Server with an ODBC connection
I have a table like this:
id PhoneNumber Caller
--------------------------------
1 915869850 John
2 912586985 Mary
3 963285874 John
4 915869850 Richard
5 965878965 James
6 925869753 Richard
8 963285874 James
and I need to be add a column that identifies the first time a phone number is called and ignores it on the subsequent times...
Like this:
id PhoneNumber Caller First Time
-----------------------------------------
1 915869850 John 1
2 912586985 Mary 1
3 963285874 John 1
4 915869850 Richard 0
5 965878965 James 1
6 925869753 Richard 1
8 963285874 James 0
Is it possible to do so?
Can you help me?

Use ROW_NUMBER() window function like below:
SELECT *, CASE WHEN (ROW_NUMBER() OVER
(PARTITION BY PhoneNumber ORDER BY id))=1 THEN 1 ELSE 0 END FirstTime
FROM Src
ORDER BY id

Assuming SQL Server 2005+ you can use ROW_NUMBER and then a CASE expression:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY PhoneNumber ORDER BY id)
FROM dbo.Phones
)
SELECT id,
PhoneNumber,
[Caller],
CASE WHEN RN = 1 THEN 1 ELSE 0 END [First Time]
FROM CTE;

Another solution:
WITH FirstCallList AS
(
SELECT
MIN([id]) AS FirstIdForNumber
FROM
Calls
GROUP BY
PhoneNumber
)
SELECT
Calls.id
,Calls.PhoneNumber
,Calls.Caller
,CASE WHEN FirstCallList.FirstIdForNumber IS NULL THEN 0 ELSE 1 END AS FirstTime
FROM
Calls
LEFT OUTER JOIN FirstCallList ON Calls.id = FirstCallList.FirstIdForNumber
ORDER BY
Calls.id
;

The classic approach to this was a self join or a scalar subquery like this:
select id, PhoneNumber,
case
when id = (
select min(p2.id) from <Phones> p2
where p2.PhoneNumber = p.PhoneNumber
) then 1 else 0
end as [First Time]
from <Phones> p

Related

How to Sum (MAX values) from different value groups in same column SQL Server

I have a table like this:
Date
Consec_Days
2015-01-01
1
2015-01-03
1
2015-01-06
1
2015-01-07
2
2015-01-09
1
2015-01-12
1
2015-01-13
2
2015-01-14
3
2015-01-17
1
I need to Sum the max value (days) for each of the consecutive groupings where Consec_Days are > 1. So the correct result would be 5 days.
This is a type of gaps-and-islands problem.
There are many solutions, here is one simple one
Get the start points of each group using LAG
Calculate a grouping ID using a windowed conditional count
Group by that ID and take the highest sum
WITH StartPoints AS (
SELECT *,
IsStart = CASE WHEN LAG(Consec_Days) OVER (ORDER BY Date) = 1 THEN 1 END
FROM YourTable t
),
Groupings AS (
SELECT *,
GroupId = COUNT(IsStart) OVER (ORDER BY Date)
FROM StartPoints
WHERE Consec_Days > 1
)
SELECT TOP (1)
SUM(Consec_Days)
FROM Groupings
GROUP BY
GroupId
ORDER BY
SUM(Consec_Days) DESC;
db<>fiddle
with cte as (
select Consec_Days,
coalesce(lead(Consec_Days) over (order by Date), 1) as next
from YourTable
)
select sum(Consec_Days)
from cte
where Consec_Days <> 1 and next = 1
db<>fiddle

How do you dynamically Assing Unique Code for every row iin hierarchical table?

My table.
Table1
Id ParentId Name Code
1 Null John
2 1 Harry
3 1 Mary
4 2 Emma
5 2 Kyle
6 4 Robert
7 Null Rohit
I want to assign each individual with the the following format unique hierarchy codes
Output Required
Id ParentId Name Code
1 Null John 1
2 1 Harry 1.1
3 1 Mary 1.2
4 2 Emma 1.1.1
5 2 Kyle 1.1.2
6 4 Robert 1.1.1.1
7 Null Rohit 2
and so on.
I hope I've got this correctly...
You can use a recursive CTE together with ROW_NUMBER() in order to create your codes.
DECLARE #dummy TABLE(Id INT,ParentId INT,[Name] VARCHAR(100));
INSERT INTO #dummy(Id,ParentId,[Name]) VALUES
(1,Null,'John')
,(2,1 ,'Harry')
,(3,1 ,'Mary')
,(4,2 ,'Emma')
,(5,2 ,'Kyle')
,(6,4 ,'Robert')
,(7,Null,'Rohit');
WITH recCTE AS
(
SELECT Id,ParentId,[Name]
,CONCAT(N'.',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code
FROM #dummy WHERE ParentId IS NULL
UNION ALL
SELECT d.Id,d.ParentId,d.[Name]
,CONCAT(r.Code,N'.', ROW_NUMBER() OVER(ORDER BY d.Id))
FROM #dummy d
INNER JOIN recCTE r ON d.ParentId=r.Id
)
SELECT Id,ParentId,[Name]
,STUFF(Code,1,1,'') AS Code
FROM RecCTE;
The idea in short:
We pick the rows with ParentId IS NULL and give them a running number.
Now we go iteratively through them (it's a hidden RBAR actually) and call their children, again with a running number.
This we do until there is nothing left.
the final SELECT needs a STUFF in order to to get rid of the first dot.
And with an extension like this, you can create an alphanumerically sortable code:
WITH recCTE AS
(
SELECT Id,ParentId,[Name]
,CONCAT(N'.',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code
,CONCAT(N'000',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code2
FROM #dummy WHERE ParentId IS NULL
UNION ALL
SELECT d.Id,d.ParentId,d.[Name]
,CONCAT(r.Code,N'.', ROW_NUMBER() OVER(ORDER BY d.Id))
,CONCAT(r.Code2,RIGHT(CONCAT('0000',ROW_NUMBER() OVER(ORDER BY d.Id)),4))
FROM #dummy d
INNER JOIN recCTE r ON d.ParentId=r.Id
)
SELECT Id,ParentId,[Name]
,STUFF(Code,1,1,'') AS Code
,Code2
FROM RecCTE
ORDER BY Code2;

select company 2's value if exists, company 1's value if not

Windows Server 2012, MS SQL Server
I know there's a set-based way to do this, but I can't figure out how to concisely phrase my question to get a useful google answer.
tblConfig
companyid var val
---------------------------------------------------------
1 fruit orange
1 game Monopoly
1 book Joyland
1 actor Ernest Thesiger
1 condiment ketchup
2 fruit apple
2 book Revival
3 actor Colin Clive
3 condiment relish
3 fruit kiwi
3 book Tales From a Buick8
I would like to select company 2's values (or 3, or 4, or n...), plus company 1's values where 2 doesn't have one (order doesn't matter), as in:
2 fruit apple
1 game Monopoly
2 book Revival
1 actor Ernest Thesiger
1 condiment ketchup
I've looked at this answer and thought I could make it work, but it eludes me. I just end up with a list of all values in the table.
You are looking for a prioritization query. In SQL Server, you can do this using row_number():
select t.*
from (select t.*,
row_number() over (partition by var
order by (case when companyid = 2 then 1
when companyid = 1 then 2
end)
) as seqnum
from t
) t
where seqnum = 1;
The logic for prioritization is in the order by clause for row_number().
Declare #YourTable table (companyid int,var varchar(50), val varchar(50))
Insert into #YourTable values
(1,'fruit','orange'),
(1,'game','Monopoly'),
(1,'book','Joyland'),
(1,'actor','Ernest Thesiger'),
(1,'condiment','ketchup'),
(2,'fruit','apple'),
(2,'book','Revival'),
(3,'actor','Colin Clive'),
(3,'condiment','relish'),
(3,'fruit','kiwi'),
(3,'book','Tales From a Buick8')
;with cteBase as (
Select *
,RowNr=Row_Number() over (Partition By var order by companyid Desc)
From #YourTable
Where companyid<=2
)
Select * from cteBase where RowNr=1
Returns
companyid var val RowNr
1 actor Ernest Thesiger 1
2 book Revival 1
1 condiment ketchup 1
2 fruit apple 1
1 game Monopoly 1

sql select from multiple records only the most recent

i have a table named customer_age that loks like this:
ID 1 2 3 4 5 6 7 8 9
NAME JIM JIM JIM NICK NICK NICK Paul Paul Paul
VALUE 20 13 12 10 20 8 4 24 14
and i want to display only the first record from each name. Something like this
ID 1 4 7
NAME JIM NICK Paul
VALUE 20 10 4
So far i have not been able to work it out.
i use sql server 2005
Any help would be appreciated...
Try using a subselect to find the lowest ID for each name, and use that set of IDs to pull the records from the main table:
SELECT ID, Name, Value
FROM customer_age
WHERE ID IN
(
SELECT MIN(ID) AS ID
FROM customer_age
GROUP BY Name
)
Just select the first record for each name using cross apply:
SELECT
ca.ID, ca.NAME, ca.VALUE
FROM customer_age c
CROSS APPLY (SELECT TOP 1 ID, NAME, VALUE
FROM customer_age ca
WHERE ca.NAME = c.NAME ORDER BY ID) ca
ORDER BY ca.ID
How about using window functions??
SELECT Id, Name, Value
FROM (
SELECT Id, Name, Value, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Id ASC) AS rowNum
FROM customer_age
) AS sub
WHERE rowNum = 1
Assuming first record means highest ID, you may try your query with descending orderby ID and TOP n.

sql query to display the input data

I want a SQL query to display the following data
Input Data:
ID GroupID Data
1 1 Hello
2 1 Null
3 1 Null
4 1 World
5 2 Niladri
6 2 XXX
7 2 Null
8 2 PPP
9 2 Null
10 2 Null
11 2 Null
12 2 LLL
as
Output Data:
GroupID MergedData
1 Hello2World
2 NiladriXXX1PPP3LLL
I need to group the data on GroupID and display the result as Hello2World
-->Hello is related to GroupID 1
-->2 is count of NULLS
-->World is related to GroupID 1
Similarly for GroupID 2.
Kindly suggest?
Thanks
As you did not mention your DBMS, this is a solution for PostgreSQL:
SELECT groupid,
string_agg(temp_data,'')
FROM (
SELECT id,
groupid,
data,
CASE
WHEN data IS NULL
THEN cast(max(rn) over (partition by groupid, data) as varchar)
ELSE data
END AS temp_data,
row_number() over (partition by groupid, data) as group_rn
FROM (
SELECT id,
groupid,
data,
CASE
WHEN data IS NULL
THEN row_number() over (partition by groupid,data)
ELSE NULL
END AS rn
FROM foo
) t1
ORDER BY id
) t2
WHERE group_rn in (0,1)
GROUP BY groupid
If your DBMS supports ANSI windowing functions and has something similar like the string_agg() function then this should be portable to your DBMS as well.

Resources