Select rows with the same field values - sql

How can I query only the records that show up twice in my table?
Currently my table looks something like this:
Number Date RecordT ReadLoc
123 08/13/13 1:00pm N Gone
123 08/13/13 2:00pm P Home
123 08/13/13 3:00pm N Away
123 08/13/13 4:00pm N Away
I need a query that will select the records that have the same 'Value' in the RecordT field and the same 'Value' in the ReadLoc field.
So my result for the above would show with the query:
Number Date RecordT ReadLoc
123 08/13/13 3:00pm N Away
123 08/13/13 4:00pm N Away
I was trying to do a subselect like this:
SELECT t.Number, t.Date, n.RecordT, n.ReadLoc
FROM Table1 t join Table2 n ON t.Number = n.Number
WHERE t.Number IN (SELECT t.Number FROM Table1 GROUP BY t.Number HAVING COUNT(t.Number) > 1 )
AND n.ReadLoc IN (SELECT n.ReadLoc FROM Table2 GROUP n.ReadLoc HAVING COUNT(n.ReadLoc) > 1 )

SELECT a.*
FROM Table1 a
JOIN (SELECT RecordT, ReadLoc
FROM Table1
GROUP BY RecordT, ReadLoc
HAVING COUNT(*) > 1
)b
ON a.RecordT = b.RecordT
AND a.ReadLoc = b.ReadLoc
SQL Fiddle

Shouldn't this work:
select *
from table1
where (RecordT, ReadLoc) in
(select RecordT, ReadLoc
from table1
group by RecordT, ReadLoc
having count(*) > 1)

The following can be taken as a base:
;with cte as (
select *, cnt = count(1) over (partition by RecordT, ReadLoc)
from TableName
)
select *
from cte
where cnt > 1
If your TableName is actually a view of two joined tables, try:
;with TableName as (
SELECT t.Number, t.Date, n.RecordT, n.ReadLoc
FROM Table1 t
join Table2 n ON t.Number = n.Number
),
cte as (
select Number, Date, RecordT, ReadLoc,
cnt = count(1) over (partition by RecordT, ReadLoc)
from TableName
)
select Number, Date, RecordT, ReadLoc
from cte
where cnt > 1 /* and RecordT='N' and ReadLoc='AWAY' */

Related

update all rows of a table based on minimum value of its group

I have a table like this
Date----- ----------Value--------- Group <br>
2017-01-01--------10--------------1--<br>
2017-01-02---------9---------------1--<br>
2017-01-03 --------5---------------2--<br>
2017-01-04 --------4---------------2--<br>
i want to update all value column in the table such that it is set to minimum date's value in that group
like this
Date----- ----------Value--------- Group <br>
2017-01-01--------10--------------1--<br>
2017-01-02---------10---------------1--<br>
2017-01-03 --------5---------------2--<br>
2017-01-04 --------5---------------2--<br>
Here you go, 2 sub-queries, the first to calculate min date per group then join back to original table to get the associated value. Then finally join this to the original table to update all associated groups with that value:
UPDATE M SET M.Value = RESULT.Value FROM MyTable M
INNER JOIN (
SELECT MV.Group, M.Value FROM MyTable M
INNER JOIN (
SELECT MIN(Date) as MinDateValue, Group FROM MyTable
GROUP BY Group
) MV ON MV.MinDateValue = M.Date AND MV.Group = M.Group
) RESULT ON RESULT.Group = M.Group
First get min date and value from sub query.Based on this result update main table
CREATE TABLE #Table(_Date Date,value INT,_Group INT)
INSERT INTO #Table(_Date ,value ,_Group)
SELECT '2017-01-01',10,1 UNION ALL
SELECT '2017-01-02',9,1 UNION ALL
SELECT '2017-01-03',5,2 UNION ALL
SELECT '2017-01-04',4,2
UPDATE #Table SET value = _Output._Value
FROM
(
SELECT A._Date , A._Group , T.value _Value
FROM #Table T
JOIN
(
SELECT MIN(_Date) _Date ,_Group
FROM #Table
GROUP BY _Group
) A ON A._Date = T._Date
) _Output WHERE _Output._Group = #Table._Group
SELECT * FROM #Table
You can also use a CTE.
Query
;with cte as(
select [rn] = row_number() over(
partition by [Group]
order by [Date]
), *
from [your_table_name]
)
update t1
set t1.[Value] = t2.[Value]
from cte t1
join cte t2
on t1.[Group] = t2.[Group]
and t1.[rn] > t2.[rn];

SQL(Need to print all the duplicate value IDs)

Empid----Name
1 aa
2 bb
3 cc
4 aa
5 bb
I need to get output to print EmpId number for which names are repeated
output Required:
1,2,4,5.
If you are using sql server,use the below script.
;WITH CTE_1 AS
(
SELECT *,COUNT(1)OVER(PARTITION BY Name ORDER BY Name) CNT
FROM [YourTable]
)
SELECT ID
FROM [CTE_1]
WHERE CNT > 1
Try this
select empid from table
where name in (select name from table group by name having count(*)>1)
SELECT *
FROM table AS parent
WHERE EXISTS(
SELECT *
FROM table AS sub
WHERE sub.Name == parent.Name && parent.Empid <> sub.Empid
)
Try this.
select distinct t.Empid from
#Your_Table t inner join
(
select Name, COUNT (Name) as count
from #Your_Table
group by Name
having COUNT (Name) > 1
)a on a.Name=t.Name
order by t.Empid
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) RowNo,*
From Your_Table
) a
WHERE RowNo > 1

Get average time between record creation

So I have data like this:
UserID CreateDate
1 10/20/2013 4:05
1 10/20/2013 4:10
1 10/21/2013 5:10
2 10/20/2012 4:03
I need to group by each user get the average time between CreateDates. My desired results would be like this:
UserID AvgTime(minutes)
1 753.5
2 0
How can I find the difference between CreateDates for all records returned for a User grouping?
EDIT:
Using SQL Server 2012
Try this:
SELECT A.UserID,
AVG(CAST(DATEDIFF(MINUTE,B.CreateDate,A.CreateDate) AS FLOAT)) AvgTime
FROM #YourTable A
OUTER APPLY (SELECT TOP 1 *
FROM #YourTable
WHERE UserID = A.UserID
AND CreateDate < A.CreateDate
ORDER BY CreateDate DESC) B
GROUP BY A.UserID
This approach should aslo work.
Fiddle demo here:
;WITH CTE AS (
Select userId, createDate,
row_number() over (partition by userid order by createdate) rn
from Table1
)
select t1.userid,
isnull(avg(datediff(second, t1.createdate, t2.createdate)*1.0/60),0) AvgTime
from CTE t1 left join CTE t2 on t1.UserID = t2.UserID and t1.rn +1 = t2.rn
group by t1.UserID;
Updated: Thanks to #Lemark for pointing out number of diff = recordCount - 1
since you're using 2012 you can use lead() to do this
with cte as
(select
userid,
(datediff(second, createdate,
lead(CreateDate) over (Partition by userid order by createdate)
)/60) datdiff
From table1
)
select
userid,
avg(datdiff)
from cte
group by userid
Demo
Something like this:
;WITH CTE AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY CreateDate) RN,
UserID,
CreateDate
FROM Tbl
)
SELECT
T1.UserID,
AVG(DATEDIFF(mi, ISNULL(T2.CreateDate, T1.CreateDate), T1.CreateDate)) AvgTime
FROM CTE T1
LEFT JOIN CTE T2
ON T1.UserID = T2.UserID
AND T1.RN = T2.RN - 1
GROUP BY T1.UserID
With SQL 2012 you can use the ROW_NUMBER function and self-join to find the "previous" row in each group:
WITH Base AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY CreateDate) RowNum,
UserId,
CreateDate
FROM Users
)
SELECT
B1.UserID,
ISNULL(
AVG(
DATEDIFF(mi,B2.CreateDate,B1.CreateDate) * 1.0
)
,0) [Average]
FROM Base B1
LEFT JOIN Base B2
ON B1.UserID = B2.UserID
AND B1.RowNum = B2.RowNum + 1
GROUP BY B1.UserId
Although I get a different answer for UserID 1 - I get an average of (5 + 1500) / 2 = 752.
This only works in 2012. You can use the LEAD analytic function:
CREATE TABLE dates (
id integer,
created datetime not null
);
INSERT INTO dates (id, created)
SELECT 1 AS id, '10/20/2013 4:05' AS created
UNION ALL SELECT 1, '10/20/2013 4:10'
UNION ALL SELECT 1, '10/21/2013 5:10'
UNION ALL SELECT 2, '10/20/2012 4:03';
SELECT id, isnull(avg(diff), 0)
FROM (
SELECT id,
datediff(MINUTE,
created,
LEAD(created, 1, NULL) OVER(partition BY id ORDER BY created)
) AS diff
FROM dates
) as diffs
GROUP BY id;
http://sqlfiddle.com/#!6/4ce89/22

How to filter out records grouped by date with a large date difference

I have some records, grouped by name and date.
I would like to find any records in a table that have a date difference between them larger than a week, from the most recent record.
Would this be possible to do with a cte?
I am thinking something along these lines (it is difficult to explain)
; with mycte as (
select *
from #GroupedRecords)
select *
from mycte a
join (select *
from #GroupedRecords) b on a.Name = b.Name
where datediff(day, a.DateCreated, b.DateCreated) > 7
For example:
Id Name Date
1 Foo 02/03/2010
2 Bar 23/02/2010
3 Ram 21/01/2010
4 Foo 29/02/2010
5 Foo 22/02/2010
6 Foo 05/12/2009
The results should be:
Id Name Date
1 Foo 02/03/2010
5 Foo 22/02/2010
6 Foo 05/12/2009
You can try:
SELECT id,
name,
DATE
FROM groupedrecords AS gr1
WHERE ( (SELECT MAX(DATE) AS md
FROM groupedrecords gr2
WHERE gr1.name = gr2.name) - gr1.DATE ) > 7;
Or probably better yet:
SELECT id,
name,
DATE
FROM groupedrecords AS gr1
INNER JOIN (SELECT name,
MAX(DATE) AS md
FROM groupedrecords AS gr2
GROUP BY name) AS q1
ON gr1.name = q1.name
WHERE ( q1.md - gr1.DATE ) > 7;
UPDATE: As suggested in the comments, here is a version that uses union to get the id with the max date per group AND the ids of those that are 7 days or older than the max date. I used a CTE for fun, it was not necessary. Note that if there is more than 1 ID that shares the max date in a group, this query will need to be modified-
WITH CTE
AS (SELECT name,
Max(date) AS MD
FROM Records
GROUP BY name)
SELECT R.ID,
R.name,
R.date
FROM CTE
INNER JOIN Records AS R
ON CTE.Name = R.Name
AND CTE.MD = R.date
UNION ALL
SELECT r1.id,
r1.name,
r1.DATE
FROM Records AS R1
INNER JOIN CTE
ON CTE.name = R1.name
WHERE ( CTE.md - R1.DATE ) > 7
ORDER BY name ASC,
date DESC
I wonder if this gets close to a solution:
; with tableWithRow as (
select *, row_number() over (order by name, date) as rowNum
from t
)
select t1.*, t2.id t2id, t2.name t2name, t2.date t2date, t2.rowNum t2rowNum
from tableWithRow t1
join tableWithRow t2
on t1.rowNum = t2.rowNum + 1 and t1.name = t2.name

SQL Server query to select local maximums

I have this data. I need to get the lowest $ full rows for each person.
Amount Date Name
$123 Jun 1 Peter
$120 Jun 5 Peter
$123 Jun 5 Paul
$100 Jun 1 Paul
$220 Jun 3 Paul
The result of the SQl Server query should be:
$120 Jun 5 Peter
$100 Jun 1 Paul
SQL Server 2005+ Version
;WITH CTE AS
(
SELECT
Amount, [Date], Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY [Amount]) AS RowNum
FROM Table
)
SELECT *
FROM CTE
WHERE RowNum = 1
Alternative Version
SELECT t.Amount, t.[Date], t.Name
FROM
(
SELECT Name, MIN(Amount) AS MinAmount
FROM Table
GROUP BY Name
) m
INNER JOIN Table t
ON t.Name = m.Name
AND t.Amount = m.Amount
One way which works on SQL Server 7 and up
select t1.*
from(select min(amount) Minamount,name
from Yourtable
group by name) t2
join Yourtable t1 on t1.name = t2.name
and t1.amount = t2.Minamount
There are a couple of ways to solve this, see here: Including an Aggregated Column's Related Values
SELECT * FROM TableName T1 WHERE NOT EXISTS
(SELECT * FROM TableName T2
WHERE T2.Name = T1.Name AND T2.Amount < T1.Amount)
In the event of ties, both rows will be shown in this scenario.
Group on the person to get the lowest amount for each person, then join the table to get the date for each row:
select y.Amount, y.Date, y.Name
from (
select min(Amount), Name
from TheTable
group by Name
) x
inner join TheTable y on x.Name = y.Name and x.Amount = y.Amount
If the amount can exist on more than one date for a person, pick one of the dates, for example the first:
select y.Amount, min(y.Date), y.Name
from (
select min(Amount), Name
from TheTable
group by Name
) x
inner join TheTable y on x.Name = y.Name and x.Amount = y.Amount
group by y.Amount, y.Name
Not quite the most efficient possible, but simpler to read:
SELECT DISTINCT [Name], [Date], MIN([Amount]) OVER(PARTITION BY [Name])
FROM #Table