T-SQL Join with "max" filter - sql

I'm using SQL Server 2014.
I'd like the following to give me no duplicate phone ID's or numbers:
WITH Phones as
(
SELECT * FROM (VALUES
(1,'602 600 8000'),
(2,'602 600 8001'),
(3,'602 600 8002')
) AS Vict_t (Id,Number)
), InvoicePhones as
(
SELECT * FROM (VALUES
(10, 1, 100, 'Alpha'),
(11, 1, 101, 'Bravo'),
(12, 1, 102, 'Charlie'),
(13, 2, 103, 'Alpha'),
(14, 2, 104, 'Bravo'),
(15, 2, 105, 'Charlie'),
(16, 3, 106, 'Alpha'),
(17, 3, 107, 'Bravo'),
(18, 3, 108, 'Charlie')
) as ip_t (Id,PhoneId,VoiceId, Name)
), Voices as
(
SELECT * FROM (VALUES
(100, '201701'),
(101, '201702'),
(102, '201703'),
(103, '201704'),
(104, '201705'),
(105, '201706'),
(106, '201708'),
(107, '201709'),
(108, '201710')
) AS Voices_t (Id,BillingCycle)
)
SELECT P.Id PhoneId, P.Number, IP.Name
FROM Phones P
LEFT JOIN InvoicePhones IP on IP.PhoneId = P.Id and IP.VoiceId =
(
select TOP 1 id
from Voices V
where V.Id = IP.VoiceId
order by V.BillingCycle desc
)
I cannot understand why the sub-select is not eliminating the duplicates.
What I'm receiving is this:
1 602 600 8000 Alpha
1 602 600 8000 Bravo
1 602 600 8000 Charlie
2 602 600 8001 Alpha
2 602 600 8001 Bravo
2 602 600 8001 Charlie
3 602 600 8002 Alpha
3 602 600 8002 Bravo
3 602 600 8002 Charlie
What I'm expecting is this:
1 602 600 8000 Charlie
2 602 600 8001 Charlie
3 602 600 8002 Charlie
This example uses simple integer ID's, but the real tables I'm working with are using uniqueidentifier. Thus the answer I need must take that into account.
I tried both versions of this accepted answer, but it doesn't work for me.
What am I missing?
Update
In addition to the answer I chose, I realized another way to solve this problem is as follows:
SELECT P.Id PhoneId, P.Number, IP.Name
FROM Phones P
LEFT JOIN InvoicePhones IP on IP.PhoneId = P.Id and IP.VoiceId =
(
select TOP 1 V.Id
from Voices V
INNER JOIN InvoicePhones IPS ON IPS.VoiceId = V.Id
WHERE P.Id = IPS.PhoneId
order by V.BillingCycle desc
)
I'm curious if they can also be solved with an OUTER APPLY, as mentioned in this other SO post

Looks like you need use ROW_NUMBER() to get the latest ID, but the final logic or the need for the third table isnt clear.
SQL DEMO
), filter as (
SELECT P.Id PhoneId, P.Number, IP.Name, IP.VoiceId,
ROW_NUMBER() OVER (PARTITION BY P.Id ORDER BY VoiceID DESC) as rn
FROM Phones P
LEFT JOIN InvoicePhones IP on IP.PhoneId = P.Id
)
SELECT *
FROM filter
WHERE rn = 1
OUTPUT
To include the third table:
SQL DEMO
), filter as (
SELECT P.Id PhoneId, P.Number, IP.Name, IP.VoiceId, V.*,
ROW_NUMBER() OVER (PARTITION BY P.Id ORDER BY BillingCycle DESC) as rn
FROM Phones P
LEFT JOIN InvoicePhones IP on IP.PhoneId = P.Id
LEFT JOIN Voices V on V.Id = IP.VoiceId
)
SELECT *
FROM filter
WHERE rn = 1
OUTPUT

I'm not sure what you want. But you have this condition:
IP.VoiceId = (select TOP 1 id
from Voices V
where V.Id = IP.VoiceId
order by V.BillingCycle desc
)
The correlation clause is V.id = IP.VoiceId. The equality comparison is -- essentially -- IP.VoiceId = V.id. They are the same. As long as there is at least one matching record in Voices, then the IP record passes the test. With your data, all IP records pass the test.
I'm not sure what you are really trying to accomplish. If you want only one row per phone, then I would be thinking EXISTS or IN.
The simplest way to get the results you want is:
select p.*
from phones p;
I suspect that you want more sophisticated logic, however.

;WITH Phones as
(
SELECT * FROM (VALUES
(1,'602 600 8000'),
(2,'602 600 8001'),
(3,'602 600 8002')
) AS Vict_t (Id,Number)
), InvoicePhones as
(
SELECT * FROM (VALUES
(10, 1, 100, 'Alpha'),
(11, 1, 101, 'Bravo'),
(12, 1, 102, 'Charlie'),
(13, 2, 103, 'Alpha'),
(14, 2, 104, 'Bravo'),
(15, 2, 105, 'Charlie'),
(16, 3, 106, 'Alpha'),
(17, 3, 107, 'Bravo'),
(18, 3, 108, 'Charlie')
) as ip_t (Id,PhoneId,VoiceId, Name)
), Voices as
(
SELECT * FROM (VALUES
(100, '201701'),
(101, '201702'),
(102, '201703'),
(103, '201704'),
(104, '201705'),
(105, '201706'),
(106, '201708'),
(107, '201709'),
(108, '201710')
) AS Voices_t (Id,BillingCycle)
)
,Expected
AS
(
SELECT P.Id PhoneId, P.Number, IP.Name
FROM Phones P
LEFT JOIN InvoicePhones IP on IP.PhoneId = P.Id and IP.VoiceId =
(
SELECT TOP 1 id
FROM Voices V
WHERE V.Id = IP.VoiceId
ORDER BY V.BillingCycle desc
)
)
SELECT PhoneId,Number,Name From
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY Phoneid,Number ORDER BY Phoneid)AS Seq
FROM Expected
)DT
WHERE DT.Seq=3
OutPut
PhoneId Number Name
------------------------------
1 602 600 8000 Charlie
2 602 600 8001 Charlie
3 602 600 8002 Charlie

Related

How can we select distinct *, From and get only Distinct IDs?

I have a small dataset that looks like this.
SELECT *
INTO Order_Table
FROM (VALUES
(1, 456, 'repair', 'House'),
(2, 456, 'paint', 'House'),
(3, 678, 'repair', 'Fence'),
(4, 789, 'repair', 'House'),
(5, 789, 'paint', 'House'),
(6, 789, 'repair', 'Fence'),
(7, 789, 'paint', 'Fence')
)
v (OrderNum, CustomerNum, OrderDesc, Structure)
SELECT *
INTO Veg_Table
FROM (VALUES
(1, '12/01/2020'),
(2, '12/02/2020'),
(3, '12/03/2020'),
(4, '12/04/2020'),
(5, '12/05/2020'),
(6, '12/06/2020'),
(7, '12/07/2020'),
(1, '12/10/2020'),
(2, '12/11/2020'),
(3, '12/12/2020')
)
v (ID, MyDate)
I have a query that looks something like this...
Select Distinct CTE.ID, *
From (
Select *
From Order_Table as Hist
Inner Join Veg_Table As Veg
On Hist.OrderNum = Veg.ID) as CTE
How can this query be modified to give only unique IDs? I always get duplicate IDs.
I also tried: Where In (Select Distinct ID From Event_View)
That didn't work either.
I want to end up with something like this.
OrderNum CustomerNum OrderDesc Structure ID MyDate
1 456 repair House 1 12/1/2020
2 456 paint House 2 12/2/2020
3 678 repair Fence 3 12/3/2020
4 789 repair House 4 12/4/2020
5 789 paint House 5 12/5/2020
6 789 repair Fence 6 12/6/2020
7 789 paint Fence 7 12/7/2020
I suppose Row_Number() Over (Partition By ID) would do it, but I was hoping for a simpler solution using 'Distinct'.
Using a regular GROUP BY and MIN appears to give you what you want.
SELECT Hist.OrderNum, Hist.CustomerNum, Hist.OrderDesc, Hist.Structure, MIN(Veg.MyDate)
FROM #Order_Table AS Hist
INNER JOIN #Veg_Table AS Veg ON Hist.OrderNum = Veg.ID
GROUP BY Hist.OrderNum, Hist.CustomerNum, Hist.OrderDesc, Hist.Structure;

Select rows using group by and in each group get column values based on highest of another column value

I need to get latest field based on another field in group by
we have
Table "SchoolReview"
Id
SchoolId
Review
Point
1
1
rv1
8
2
1
rv2
7
3
2
rv3
4
4
2
rv4
7
5
3
rv5
2
6
3
rv6
8
I need to group by SchoolId and the inside group I need to get Review and Point from highest "Id" column.
I dont need "Id" coulmn but even if I get it for this solution its okay.
Result I am looking for shall look like this.
SchoolId
Review
Point
1
rv2
7
2
rv4
7
3
rv6
8
Any one experienced in MS SQL Server can help in this regard?
Using sample data from other answer
SELECT *
INTO #Data
FROM (VALUES
(1, 1, 'rv1', 8),
(2, 1, 'rv2', 7),
(3, 2, 'rv3', 4),
(4, 2, 'rv4', 7),
(5, 3, 'rv5', 2),
(6, 3, 'rv6', 8)
) v (Id, SchoolId, Review, Point)
SELECT S.SchoolId,
S.Review,
S.Point
FROM #Data S
INNER JOIN
(
SELECT Id = MAX(S1.Id),
S1.SchoolId
FROM #Data S1
GROUP BY SchoolId
) X ON X.Id = S.Id AND X.schoolId = S.SchoolId
ORDER BY X.SchoolId
;
output
You do not need to group the rows, you simply need to select the appropriate rows from the table. In this case, using ROW_NUMBER() is an option:
Table:
SELECT *
INTO Data
FROM (VALUES
(1, 1, 'rv1', 8),
(2, 1, 'rv2', 7),
(3, 2, 'rv3', 4),
(4, 2, 'rv4', 7),
(5, 3, 'rv5', 2),
(6, 3, 'rv6', 8)
) v (Id, SchoolId, Review, Point)
Statement:
SELECT SchoolId, Review, Point
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY SchoolId ORDER BY Id DESC) AS Rn
FROM Data
) t
WHERE Rn = 1
Result:
SchoolId Review Point
---------------------
1 rv2 7
2 rv4 7
3 rv6 8

SQL - How to get Treeview of GrandParent, Parent, Child by ID

I have 2 tables, where Customer table has customer data and relations table has relations of customer.
CREATE Table Customer
(
id int,
name nvarchar(10)
)
INSERT INTO Customer Values
(1, 'aaa'),
(2, 'bbb'),
(3, 'ccc'),
(4, 'ddd'),
(5, 'eee'),
(6, 'fff'),
(7, 'ggg'),
(8, 'hhh'),
(9, 'iii'),
(10, 'jjj'),
(11, 'kkk'),
(12, 'lll')
CREATE TABLE Relations
(
id int,
parentid int
)
INSERT INTO Relations VALUES
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 1),
(7, 4),
(8, 5),
(9, 8),
(10, 8),
(12, 7)
I want to find GrandParent, Parent and child by ID. For Ex: If I want to find all relations of ID=4, I should get the result in the below format. Ordered by Grand Parent at the top if the ID has a parent or Grand parent. If not, then it has to show the child of that ID.
Grand Parent | aaa
Parent | bbb
Child | ggg
Child | lll
Could you please help me with the above query in "SQL Server".
You can use recursives CTEs (Common Table Expressions) to get this information. For example, for ID = 4 you can do:
with
a as ( -- get ancestors
select 0 as lvl, id, name from customer where id = 4
union all
select a.lvl - 1, c.id, c.name
from a
join relations r on a.id = r.id
join customer c on c.id = r.parentid
),
d as ( -- get descendants
select 0 as lvl, id, name from customer where id = 4
union all
select d.lvl + 1, c.id, c.name
from d
join relations r on d.id = r.parentid
join customer c on c.id = r.id
)
select lvl, id, name from a
union
select lvl, id, name from d
order by lvl
Result:
lvl id name
--- -- ----
-2 1 aaa
-1 2 bbb
0 4 ddd
1 7 ggg
2 12 lll

SQL sort and last record

this is my first post so pardon me if my question is not in it's appropriate places or tittle
I have a table like this
ID DATE Cat VALUE
-------------------------
1 07/07/2018 A 100
2 07/07/2018 A 200
3 07/07/2018 B 300
4 07/07/2018 B 400
5 07/07/2018 C 500
6 07/07/2018 C 600
7 08/07/2018 A 700
8 08/07/2018 A 800
9 08/07/2018 B 900
10 08/07/2018 B 110
11 08/07/2018 C 120
I would like to return
distinct category, sum of value, last record of the category
something like this
Cat sumValue lastrecord
--------------------------
A 1800 800
B 1710 110
C 1220 120
is it possible to do it in a single query
thanks
I am able to find the SUM
SELECT cat, SUM(value) FROM table GROUP BY cat;
and
find the last ID (autonumber key) using MAX
SELECT MAX(ID), cat FROM table GROUP BY cat;
but i just can't get the value for the last record
SQLFiddle
SELECT
t.cat,
SUM(t.value) as sumValue,
(
SELECT
t3.value
FROM
`table` t3
WHERE
t3.id = MAX(t2.id)
) as lastrecord
FROM
`table` t
JOIN
`table` t2 ON t.id = t2.id
GROUP BY
cat
EDIT shorter Version:
SELECT
t.cat,
SUM(t.value) as sumValue,
(SELECT value FROM `table` t2 WHERE t2.id = MAX(t.id)) lastValue
FROM
`table` t
GROUP BY
t.cat
This should do it
declare #t table (id int, cat char, value int);
insert into #t values
(1, 'A', 100),
(2, 'A', 200),
(3, 'B', 300),
(4, 'B', 400),
(5, 'C', 500),
(6, 'C', 600),
(7, 'A', 700),
(8, 'A', 800),
(9, 'B', 900),
(10, 'B', 110),
(11, 'C', 120);
select cat, value, sum
from
( select *
, sum(value) over (partition by cat) as sum
, ROW_NUMBER() over (partition by cat order by id desc) as rn
from #t
) tt
where tt.rn = 1
I hope you're looking for something like this,
Please replace the table name with your table name.
SELECT A.id,
A.cat,
A.date,
A.total_value,
A1.value
FROM (SELECT Max(id) AS id,
cat,
Max(date) AS Date,
Sum(value) AS Total_Value
FROM tbl_sof
GROUP BY cat) AS A
INNER JOIN tbl_sof A1
ON A.id = A1.id

SQL Server 2008: find number of contiguous rows with equal values

I have a table with multiple Ids. Each Id has values arranged by a sequential index.
create table myValues
(
id int,
ind int,
val int
)
insert into myValues
values
(21, 5, 300),
(21, 4, 310),
(21, 3, 300),
(21, 2, 300),
(21, 1, 345),
(21, 0, 300),
(22, 5, 300),
(22, 4, 300),
(22, 3, 300),
(22, 2, 300),
(22, 1, 395),
(22, 0, 300)
I am trying to find the number of consecutive values that are the same.
The value field represents some data that should be change on each entry (but need not be unique overall).
The problem is to find out when there are more than two consecutive rows with the same value (given the same id).
Thus I'm looking for an output like this:
id ind val count
21 5 300 1
21 4 310 1
21 3 300 2
21 2 300 2
21 1 345 1
21 0 300 1
22 5 300 4
22 4 300 4
22 3 300 4
22 2 300 4
22 1 395 1
22 0 300 1
I'm aware this is similar to the island and gaps problem discussed here.
However, those solutions all hinge on the ability to use a partition statement with values that are supposed to be consecutively increasing.
A solution that generates the ranges of "islands" as an intermediary would work as well, e.g.
id startind endind
21 3 2
22 5 2
Note that there can be many islands for each id.
I'm sure there is a simple adaptation of the island solution, but for the life of me I can't think of it.
find the continuous group and then do a count() partition by that
select id, ind, val, count(*) over (partition by id, val, grp)
from
(
select *, grp = dense_rank() over (partition by id, val order by ind) - ind
from myValues
) d
order by id, ind desc
The other solution is obviously more elegant. I'll have to study it a little closer myself.
with agg(id, min_ind, max_ind, cnt) as (
select id, min(ind), max(ind), count(*)
from
(
select id, ind, val, sum(brk) over (partition by id order by ind desc) as grp
from
(
select
id, ind, val,
coalesce(sign(lag(ind) over (partition by id, val order by ind desc) - ind - 1), 1) as brk
from myValues
) as d
) as d
group by id, grp
)
select v.id, v.ind, v.val, a.cnt
from myValues v inner join agg a on a.id = v.id and v.ind between min_ind and max_ind
order by v.id, v.ind desc;