Selecting the newest records for 6 unique columns - sql

I have a table of 6 currency conversions, it's updated almost daily. Unfortunately the way the software works is it inserts new rows rather than updating the existing ones. My previous SELECT was as follows
SELECT FROM_CURRENCY_ID, XCHG_RATE
FROM
(
SELECT TOP 6 FROM_CURRENCY_ID, XCHG_RATE
FROM SHARED_CURRENCY_EXCHANGE
WHERE NOT FROM_CURRENCY_ID = 'CAD'
ORDER BY RECORD_CREATED desc
) t
ORDER BY FROM_CURRENCY_ID
The issue now is some records got updated while others didn't so my query returns duplicate values for one of the currencys and nothing for one. I need it to output the 6 unique FROM_CURRENCY_IDs and their XCHG_RATE with the newest RECORD_CREATED dates
I've been trying a group by to exclude the duplicate rows with no luck.

with x as
(select row_number() over(partition by from_currency_id order by record_created desc) rn, * from shared_currency_exchange)
select from_currency_id, xchg_rate from x
where rn = 1
This gives the most recent record a rownumber 1 and you can use the cte with this condition.

Related

Using TOP and WHERE in SQL [duplicate]

This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed 3 months ago.
Is there a way to use TOP and WHERE multiple times? similar as a for loop to create a table?
I am using the following query to create a table that contains the top 26 records where the value of the column [code] is 11:
SELECT TOP 26 [date]
,[group]
,[code]
,[pol]
,[relation]
FROM [database].[table1] WHERE group in ('A','B','C',...,'Z') and code = '11'
The problem with this query is that I get 26 records with the value of the column [group] equal to A. This happens because there are thousands of records that meet that criterion.
Ideally, I would like the top 1 of each group (A to Z) with the value of code 11. I could achieve that by running the query above 26 times using TOP 1 and a different value of group, but this is impractical.
Is there any way to run this query multiple times to get the desired table?
Thanks in advance!
You can use a CTE to assign a ROW_NUMBER(), then only return rows WHERE ROW_NUMBER() = 1. You may need to adjust the PARTITION/ORDER BY depending on your data and what you're expecting your result set to be.
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [group], code ORDER BY code ASC) AS rn
FROM table1
)
SELECT *
FROM cte
WHERE rn = 1 AND code = 11
Demo here.
Assuming that your database support WITH TIES, then no need for a subquery. You can just order by with row_number:
SELECT TOP 1 WITH TIES
[date]
,[group]
,[code]
,[pol]
,[relation]
FROM [database].[table1]
WHERE group in ('A','B','C',...,'Z') and code = '11'
ORDER BY ROW_NUMBER() OVER(PARTITION BY [group] ORDER BY [date] desc)
You did not tell which column should be used to identify the top row per group, so I used a descending sort on the date. For each group, ROW_NUMBER assigns 1 to the row with the latest date ; TOP 1 WITH TIES then selects all such rows.

How create a unique ID based on conditions in SQL?

I would like to get a new ID, no matter the format (in the example below 11,12,13...)
Based on the following condition:
Every time the days column value is greater then 1 and not null then current row and all following ones will get the same ID until a new value will meet the condition.
Within the same email
Below you can see the expected 1 (in the format of XX)
I thought about using two conditions with the following order between them
Every time the days column value is greater then 1 then all following rows will get the same ID until a new value will meet the condition.
2.AND When lag (previous) is equal to 0/1/null.
Assuming you have an EmailDate column over which you're ordering (a DATETIME field, really), try something like this:
WITH
TableNameWithEmailDateIDs AS (
SELECT
*,
ROW_NUMBER() OVER (
ORDER BY
Email DESC,
EmailDate
) AS EmailDateID
FROM
TableName
),
IDs AS (
SELECT
*,
LEAD(EmailDateID, 1) OVER (
ORDER BY
Email,
EmailDate
) AS LeadEmailDateID
FROM
(
SELECT
*,
-- REMOVE +10 if you don't want 11 to be starting ID
ROW_NUMBER() OVER (
ORDER BY
Email DESC,
EmailDate
)+10 AS ID
FROM
TableNameWithEmailDateIDs
WHERE
Days > 1
OR Days IS NULL
) X
)
SELECT
COALESCE(TableName.EmailDate, IDs.EmailDate) AS EmailDate,
IDs.Email,
COALESCE(TableName.Days, IDs.Days) AS Days,
IDs.ID
FROM
IDs
LEFT JOIN TableNameWithEmailDateIDs TableName
ON IDs.Email = TableName.Email
AND TableName.EmailDateID BETWEEN
IDs.EmailDateID
AND IDs.LeadEmailDateID-1
ORDER BY
ID DESC,
TableName.EmailDate DESC
;
First, create a CTE that generates IDs for each distinct Email/Date combo (helpful for LEFT JOIN condition later). Then, create a CTE that generates IDs for rows that meet your condition (i.e. the important rows). Finally, LEFT JOIN your main table onto that CTE to fill in the "gaps", so to speak.
I suggest running each of the components of this query independently to fully understand what's going on.
Hope it helps!

Removing duplicate rows based on one column same values but keep one record

SQL Server Version
Remove all dupe rows (row 3 thru 18) with service_date = '2018-08-29 13:05:00.000' but keep the oldest row (row 2) and of course keep row 1 since its different service_date. Don't mind the create_timestamp or document_file since it's the same customer. Any idea?
In SQL Server, we can try deleting using a CTE:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY service_date ORDER BY create_timestamp) rn
FROM yourTable
)
DELETE
FROM cte
WHERE rn > 1;
The strategy here is to assign a row number to each group of records sharing the same service_date, with 1 being assigned to the oldest record in that group. Then, we can phrase the delete by just targeting all records which have a row number greater than 1.
You don't need to use Partition function.please use the below query for efficient performance.i have tested its working fine.
with result as
(
select *, row_number() over(order by create_timestamp) as Row_To_Delete from TableName
)
delete from result where result.Row_To_Delete>2
I think you will want to remove these data per customer basis
I mean, if customers are different you will want to keep the entries even on the same date
If you you will require the addition of Customer column in partition by clause used to identify duplicate rows in SQL
By copying and modifying Tim's solution, you can check following
;WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY customer, service_date ORDER BY create_timestamp) rn
FROM yourTable
)
DELETE
FROM cte
WHERE rn > 1;

Select last duplicate row with different id Oracle 11g

I have a table that look like this:
The problem is I need to get the last record with duplicates in the column "NRODENUNCIA".
You can use MAX(DENUNCIAID), along with GROUP BY... HAVING to find the duplicates and select the row with the largest DENUNCIAID:
SELECT MAX(DENUNCIAID), NRODENUNCIA, FECHAEMISION, ADUANA, MES, NOMBREESTADO
FROM YourTable
GROUP BY NRODENUNCIA, FECHAEMISION, ADUANA, MES, NOMBREESTADO
HAVING COUNT(1) > 1
This will only show rows that have at least one duplicate. If you want to see non-duplicate rows too, just remove the HAVING COUNT(1) > 1
There are a number of solutions for your problem. One is to use row_number.
Note that I've ordered by DENUNCIID in the OVER clause. This defines the "Last Record" as the one that has the largest DENUNCIID. If you want to define it differently you'd need to change the field that is being ordered.
with dupes as (
SELECT
ROW_NUMBER() OVER (Partition by NRODENUNCIA ORDER BY DENUNCIID DESC) RN,
*
FROM
YourTable
)
SELECT * FROM dupes where rn = 1
This only get's the last record per dupe.
If you want to only include records that have dupes then you change the where clause to
WHERE rn =1
and NRODENUNCIA in (select NRODENUNCIA from dupes where rn > 1)

Update a field for a specific # of records in SQL Server 2005

Say I want 3 records flagged for each product in my table. But if some products only get 1 or 2 records flagged or even no records flagged, how can I make it randomly flag the remaining records up to the total of 3 per product.
Ex:
1 record gets flagged for Product_A, 2 records get flagged for Product_B and 3 records get flagged for Product_C.
Once script is complete, I need 2 more records flagged for Product_A and 1 more for Product_B.
This can be a loop or a cte or whatever is the most efficient way to do this in sql. Thanks!
Here's one way to do it:
;with SelectedIds as(
select
Id,
row_number() over (
partition by ProductCode -- distinct numbering for each Product Code
order by newid() -- random
) as rowno
from ProductLines
)
update p
set IsFlagged = 1
from ProductLines p
join SelectedIds s
on p.id = s.id and
s.rowno <= 3 -- limit to 3 records / product code
;
Here's a full sample, including some test data: http://www.sqlfiddle.com/#!3/3bee1/6
Use row_number() in a derived table where the numbers are generated so the rows that already have flags come first and the rest are ordered randomly and partition by Product. If random is not a requirement you can just remove newid() from the query.
Set the flag for the rows number 1-3 if the row is not already flagged.
update T
set Flag = 1
from (
select Flag,
row_number() over(partition by Product
order by Flag desc, newid()) as rn
from YourTable
) as T
where T.rn <= 3 and
T.Flag = 0
SQL Fiddle