most recent (max) date for every id - sql

a = id
b = date
c = NewestDate
For every a, I have b c
How can I get the newest date for every set of id's (MY 'a' column)? they don't all have the same end date. So I do need a max for all of them.
The data is in myTable,
so far i tried:
select *
into #myTable
from myTable
select
t.a
,t.b
,t.c
,(select max(b) from myTable) as c
from myTable t
left join #myTable t1
on t.a = t1.a and t.b = t1.b
order by a, b
The problem with the above code is that in 'c' it is placed the max date of them all, which is not what I actually want.
EDIT: the problem is now solved with the answer given by Dmitry Poliakov (thanks). Used:
SELECT a,
b,
max(b) OVER (PARTITION BY a) AS c
FROM myTable
ORDER BY a,b

you can select maximum date for each group of ids as
SELECT a,
b,
max(b) OVER (PARTITION BY a) AS c
FROM myTable
ORDER BY a,b
EDIT: one of possible solutions for the second(edited) part of question is
WITH cte AS (
SELECT a,
b,
max(b) OVER (PARTITION BY a) AS c,
d
FROM myTable
)
SELECT t1.a,
t1.b,
t1.c,
t1.d,
t2.d AS e
FROM cte t1
JOIN cte t2 ON t1.a=t2.a AND t1.c=t2.b

DECLARE #updates TABLE (a int,b date,c date)
INSERT INTO #updates VALUES
(1,GETDATE(),GETDATE()+10),
(2,GETDATE()+11,GETDATE()+13),
(2,GETDATE()+11,GETDATE()+14),
(3,GETDATE()+11,GETDATE()+13),
(1,GETDATE()+11,GETDATE()+13);
WITH
cte AS
(
SELECT a, max(c) latest
FROM #updates
GROUP BY a
)
SELECT *
FROM cte INNER JOIN #updates as d ON cte.a=d.a AND cte.latest = d.c
Builds a table to select the records with the newest updates and then joins this new table with your original table to extract all the fields in matching rows

Related

select over partiton by ... order by .. used long time

select a.id
from (select /*+index(test_table, test_index)*/
row_number() over (partition by a, b, c order by d desc) rn,
id
from test_table
) a
where a.rn = 1
test_index(a, b, c, d)
limit access to 500, cost 9s)
How should I fix it
Out of curiosity, how long does this take with a correlated subquery instead?
select t.id
from test_table t
where t.d = (select max(t2.d)
from test_table t2
where t2.a = t.a and t2.b = t.b and t2.c = t.c
);
Or using aggregation?
select max(t.id) keep (dense_rank first order by d desc)
from test_table t
group by a, b, c;

How to find duplicate rows if one of two certain columns are equal.. and a third & fourth column are equal?

Let's say I have a table with columns: A, B, C & D
Any two rows are considered a duplicate if:
A, B, C have equal values but not D
or
A, B, D have equal values but not C.
How do I get a set of duplicate rows? Using a CTE is OK.
I think you can do it with union all with the corresponding where conditions.
select * from tablename where a=b and b=c and a<>d
union all
select * from tablename where a=b and b=d and a<>c
Using a self join it's quite easy:
SELECT DISTINCT t1.*
FROM TableName t1
INNER JOIN TableName t2
ON T1.A = T2.A
AND T1.B = T2.B
AND (T1.C = T2.C OR T1.D = T2.D)
Assuming, of course, that if all 4 columns are equal it's a duplicated row as well...
However, if for some strange reason these rows are not considered as duplicates, you can change the conditions in the ON clause to this:
SELECT DISTINCT t1.*
FROM TableName t1
INNER JOIN TableName t2
ON T1.A = T2.A
AND T1.B = T2.B
AND (
(T1.C = T2.C AND T1.D <> T2.D)
OR (T1.C <> T2.C AND T1.D = T2.D)
)
You can use RANK() to detect duplicates without having to select from the table twice :
SELECT s.* FROM (
SELECT t.*,
RANK() OVER(PARTITION BY t.a,t.b,t.c ORDER BY t.d) as d_dif,
RANK() OVER(PARTITION BY t.a,t.b,t.D ORDER BY t.c) as c_dif
FROM YourTable) s
WHERE s.d_dif > 1 or s.c_dif > 1
RANK() as opposed to ROW_NUMBER() deals with duplicates, so if d / c will be the same, both records will get the same rank and won't be selected.

How to restructure SQL query so that I can write/refer them easily

I have following queries.
Select * from (
Select a, b, c, d, from t1
Union
Select a, b, c, d from t2
) where a is not null and order by b.
Now I have to fetch data from another table based on above result set.
Select * from (Select * from (
Select a, b, c, d, from t1
Union
Select a, b, c, d from t2
)
where a is not null and order by b)
as tempT1 left outer join t3 on tempT1.a = t3.a
I have to further use this result set to form another select query. So, writing in above style will be complex with time. And with time this query will be complex to read.
How to make it simple? Can I dump partial result to another table?
You can create views that will replace these inner SELECTs
From w3schools :
"In SQL, a view is a virtual table based on the result-set of an SQL statement.
A view contains rows and columns, just like a real table. The fields in a view are fields from one or more real tables in the database.
You can add SQL functions, WHERE, and JOIN statements to a view and present the data as if the data were coming from one single table."
Document them so you wont get lost and that's it...
Use temporary tables. Rewrite your queries as
Select * into #tempResult1 from (
Select a, b, c, d, from t1
Union
Select a, b, c, d from t2
) where a is not null and order by b
Now for next query, use above temp table as
Select * into #tempResult2
from #tempResult1
left outer join t3 on tempT1.a = t3.a
Now instead of writing this query, just use #tempResult2.
select * from #tempResult2
You have some wrong SQL syntax too. Try:
;
WITH Q1
AS ( SELECT a ,
b ,
c ,
d
FROM t1
UNION
SELECT a ,
b ,
c ,
d
FROM t2
),
Q2
AS ( SELECT *
FROM Q1
)
SELECT *
FROM Q2 tempT1
LEFT OUTER JOIN t3 ON tempT1.a = t3.a
WHERE a IS NOT NULL
ORDER BY b
Assuming that you are using SQL Server, You could simplify something like below. Not a compiled version. But you could use this as pseudo code.
with cte as
(
Select a, b, c, d from t1 where a is not null
Union
Select a, b, c, d from t2 where a is not null
)
select * from cte tempT1 left outer join t3 on tempT1.a = t3.a order by tempT1.b
You Can use Comman Table expression or Temp Table or Table Variable
with cte as
(
Select a, b, c, d from t1
Union
Select a, b, c, d from t2
)
,
cte2 as
(
select * cte where a is not null and order by b
)
select * from cte2 c left outer join t3 on c.a = t3.a
Using Temp Table
select * into #temp1
from
(
Select a, b, c, d from t1
Union
Select a, b, c, d from t2
)
select * into #temp2 from #temp1 where a is not null and order by b
select * from temp2 c left outer join t3 on c.a = t3.a

SQL Server. Delete from Select

I am using SQL Server 2012, and have the following query. Let's call this query A.
SELECT a.col, a.fk
FROM Table1 a
INNER JOIN (
select b.col
from Table1 b
group by b.col
having count(*) > 1)
b on b.col = a.col
I want to delete only the rows returned from query A, specifically rows that match the returned col AND fk
I am thinking of doing the following, but it will only delete rows that match on the col.
delete from Table1
where col in (
SELECT a.col
FROM Table1 a
INNER JOIN (
select b.col
from Table1 b
group by b.col
having count(*) > 1)
b on b.col = a.col)
)
Use delete from Join syntax
delete t1
from table1 t1
INNER JOIN (SELECT a.col, a.fk
FROM Table1 a
INNER JOIN (
select b.col
from Table1 b
group by b.col
having count(*) > 1)
b on b.col = a.col) t2
ON t1.col1=t2.col1 and t1.fk=t2.fk
you can combine col and fk fields to be another unique filed to retrieve wanted rows
delete from Table1
where cast(col as varchar(50))+'//'+cast(fk as varchar(50)) in (
SELECT cast(a.col as varchar(50))+'//'+cast(a.fk as varchar(50))
FROM Table1 a
INNER JOIN (
select b.col
from Table1 b
group by b.col
having count(*) > 1)
b on b.col = a.col)
)
You can express Query A like this:
SELECT col, fk
FROM (
SELECT a.col, a.fk, COUNT(*) OVER (PARTITION BY a.col) AS [count]
FROM Table1 a
) counted
WHERE [count] > 1
Which leads to a nice way to do the DELETE using a CTE:
;WITH ToDelete AS (
SELECT a.col, a.fk, COUNT(*) OVER (PARTITION BY a.col) AS [count]
FROM Table1 a
)
DELETE FROM ToDelete
WHERE [count] > 1
This does give the same result as the DELETE statement in your question though.
If you want to delete all but one row with the duplicate col value you can use something like this:
;WITH ToDelete AS (
SELECT a.col, a.fk
, ROW_NUMBER() OVER (PARTITION BY a.col ORDER BY a.fk) AS [occurance]
FROM Table1 a
)
DELETE FROM ToDelete
WHERE [occurance] > 1
The ORDER BY clause will determine which row is kept.

SELECT Row Values WHERE MAX() is Column Value In GROUP BY Query

How can I select like this? Can I create a User defined Aggregate Function
SELECT Max(A),(SELECT TOP 1 FROM TheGroup Where B=Max(A)) FROM MyTable
where MyTable as Shown Below
A B C
--------------
1 2 S
3 4 S
4 5 T
6 7 T
I want a Query Like this
SELECT MAX(A),(B Where A=Max(A)),C FROM MYTable GROUP BY C
I'm Expecting the result as below
MAX(A) Condition C
-----------------------
3 4 S
6 7 T
SELECT A,B,C FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY C ORDER BY A DESC) RN FROM MyTable)
WHERE RN = 1
(this query will always return only one row per C value)
OR
WITH CTE_Group AS
(
SELECT C, MAX(A) AS MaxA
FROM MyTable
GROUP BY C
)
SELECT g.MaxA, t.B, g.C
FROM MyTable t
INNER JOIN CTE_Group g ON t.A = g.MaxA AND t.C = g.C
(if there are multiple rows that have same Max(A) value - this query will return all of them)
Try Following Query :
SELECT TABLE1.A , TABLE2.B , TABLE1.C
FROM
(
SELECT MAX(A) AS A,C
FROM MYTable
GROUP BY C
) AS TABLE1
INNER JOIN
(
SELECT *
FROM MYTable
) AS TABLE2 ON TABLE1.A = TABLE2.A
SQLFIDDLE
you can do it by simple join query . join query always run faster then In query . Join query run only one time at the time of execution of the query . we can archive same result by using IN query .
SELECT t1.*
FROM YourTable t1
Left Outer Join YourTable t2 on t1.C=t2.C AND t1.A < t2.A
WHERE t2.A is null
how about this:
SELECT *
FROM MyTable
WHERE A IN (SELECT MAX(A) FROM MyTable GROUP BY C)
SELECT Max(A)
FROM MyTable
Where B=(SELECT Max(A) FROM MyTable)
update:
SELECT *
FROM MyTable
Where B=(SELECT Max(A) FROM MyTable)
update 2:
SELECT DISTINCT A, B
FROM MyTable
Where A=(SELECT Max(A) FROM MyTable GROUP BY C)
update 3:
ok, I think I understand what you're looking for now.. How about this:
SELECT *
FROM MyTable
Where A in (SELECT Max(A) FROM MyTable GROUP BY C)
WITH
cte AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY cola desc) AS Rno,
*
FROM
tbl
)
SELECT top 1
cola,colb
FROM
cte
order by Rno
Then try it:
WITH
cte AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY col3 ORDER BY col1 desc) AS Rno,
*
FROM
tbl
)
SELECT
col1,col2,col3
FROM
cte
WHERE Rno=1