T-SQL How to update the bottom/last row only? - tsql

I want to update the bottom/the last row in my table. I have try to implement this solution, but nothing seems as correct syntax:
UPDATE TOP(1) #ResultTable
SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)
ORDER BY PeriodID DESC
OR
UPDATE TOP(1) #ResultTable
SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)
FROM #ResultTable
ORDER BY PeriodID DESC
What I have till now working is:
UPDATE #ResultTable
SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)
WHERE PeriodID=(SELECT COUNT(PeriodID) FROM #ResultTable)-1
but this will not always works, as in my function some of the records are deleted and I am not always having PeriodIDs incremented with 1.

;WITH CTE AS
(
SELECT TOP 1 *
FROM #ResultTable
ORDER BY PeriodID DESC
)
UPDATE CTE SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)

There's not enough context in your question to give a bulletproof answer. Based on your working solution, how about instead of looking for the count look for the max PeriodID? As long as subsequent PeriodID's are a greater value it should work to get the "last" record.
UPDATE #ResultTable
SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)
WHERE PeriodID=(SELECT MAX(PeriodID) FROM #ResultTable)

If you have a unique column (perhaps PeriodID?) in each row you could do something like this:
UPDATE #ResultTable
SET PeriodLastDate=DATEADD(DAY,-1,PeriodLastDate)
where <unique column> = (select top 1 <unique column>
from #ResultTable
order by PeriodID desc
)

What about :
UPDATE ResultTable SET PeriodLastDate='NewValue' WHERE ID= (SELECT MAX(ID) FROM ResultTable)

Related

Update a deleted_at column on partition in PostgreSQL

Quick question, I'm trying to update a column only when there are duplicates(partition column > 1) in the table and have selected it based on partition concept, But the current query updates the whole table! please check the query below, Any leads would be greatly appreciated :)
UPDATE public.database_tag
SET deleted_at= '2022-04-25 19:33:29.087133+00'
FROM (
SELECT *,
row_number() over (partition by title order by created_at) as RN
FROM public.database_tag
ORDER BY RN DESC) X
WHERE X.RN > 1
Thanks very much!
Assuming that every row have unique ID it can be done like below.
UPDATE database_tag
SET deleted_at= '2022-04-25 19:33:29.087133+00'
WHERE <some_unique_id> in (
select <some_unique_id> from (
SELECT <some_unique_id>,
row_number() over (partition by title order by created_at) as RN
FROM public.database_tag
) X
WHERE X.RN > 1
)
Or we can reverse query to update all but set of ID's
UPDATE database_tag
SET deleted_at= '2022-04-25 19:33:29.087133+00'
WHERE <some_unique_id> not in (
select distinct on (title)
<some_unique_id> from database_tag
order by title, created_at
)

Update column with more than one value

I have a table tableA which looks something like this:
issue_id start_date end_date
issue1 2019-11-07 2020-04-30
issue2 2019-11-07 2020-01-28
I have to update the end_date based on the results of the query.
UPDATE tableA SET end_date =
(
SELECT max_end_date from update_end_date
)
WHERE issue_id = (SELECT issue_id FROM update_end_date);
It works when when query returns one result. However it fails when more than one results are returned which make sense. I cannot pre determine the results of the query so it might return more than one result. Is there any way if I can update the column with multiple values.
You could use correlated subquery:
UPDATE tableA
SET end_date = (SELECT max_end_date
from update_end_date
WHERE update_end_date.issue_id = tableA.issue_id)
WHERE issue_id IN (SELECT issue_id FROM update_end_date);
Another possibility to #Lukas solution is using proprietary PostgreSQL's syntax UPDATE FROM
UPDATE tablea
SET end_date = max_end_date
FROM update_end_date
WHERE tablea.issue_id = update_end_date.issue_id

How to use new created column in where column in sql?

Hi I have a query which looks like the following :
SELECT device_id, tag_id, at, _deleted, data,
row_number() OVER (PARTITION BY device_id ORDER BY at DESC) AS row_num
FROM mdb_history.devices_tags_mapping_history
WHERE at <= '2019-04-01'
AND _deleted = False
AND (tag_id = '275674' or tag_id = '275673')
AND row_num = 1
However when I run the following query, I get the following error :
ERROR: column "row_num" does not exist
Is there any way to go about this. One way I tried was to use it in the following way:
SELECT * from (SELECT device_id, tag_id, at, _deleted, data,
row_number() OVER (PARTITION BY device_id ORDER BY at DESC) AS row_num
FROM mdb_history.devices_tags_mapping_history
WHERE at <= '2019-04-01'
AND _deleted = False
AND (tag_id = '275674' or tag_id = '275673')) tag_deleted
WHERE tag_deleted.row_num = 1
But this becomes way too complicated as I do it with other queries as I have number of join and I have to select the column as stated from so it causes alot of select statement. Any smart way of doing that in a more simpler way. Thanks
You can't refer to the row_num alias which you defined in the same level of the select in your query. So, your main option here would be to subquery, where row_num would be available. But, Postgres actually has an option to get what you want in another way. You could use DISTINCT ON here:
SELECT DISTINCT ON (device_id), device_id, tag_id, at, _deleted, data
FROM mdb_history.devices_tags_mapping_history
WHERE
at <= '2019-04-01' AND
_deleted = false AND
tag_id IN ('275674', '275673')
ORDER BY
device_id,
at DESC;
Too long/ formatted for a comment. There is a reason behind #TimBiegeleisen statement "alias which you defined in the same level of the select". That reason is that all SQL statement follow the same sequence for evaluation. Unfortunately that sequence does NOT follow the sequence of clauses within the statement presentation. that sequence is in order:
from
where
group by
having
select
limits
You will notice that what actually gets selected fall well after evaluation of the where clause. Since your alias is defined within the select phase it does not exist during the where phase.

How to get second largest value in SSRS

I want second largest value. I can get first largest by using
max(field name) but how i can get 2nd largest.Is there any custom code.
I am new so any help greatly appreciated.
Thanks!
I want it in SSRS
ORDER BY your field DESC, then limit the result set to 1 row, starting at the 2nd row:
SELECT field FROM TABLE ORDER BY field DESC LIMIT 1, 1;
(This is MySQL syntax, your engine may vary.)
This is messy but It should work:
SELECT TOP 1
[column]
FROM [table]
WHERE [column] <>
(
Select top 1
[column]
from [table]
order by [column] DESC
)
ORDER BY [column] DESC
If this is in a procedure and you dont care about doing 2 queries this looks a bit neater:
DECLARE #MAX INT
SELECT TOP 1 #MAX = MAX([COLUMN]) from [table] order by [COLUMN] desc
SELECT TOP 1 [COLUMN] FROM [TABLE] WHERE [COLUMN] <> #mAX order by [COLUMN] desc

tsql math across multiple dates in a table

I have a #variabletable simply defined as EOMDate(datetime), DandA(float), Coupon(float), EarnedIncome(float)
04/30/2008, 20187.5,17812.5,NULL
05/31/2008, 24640.63, 22265.63, NULL
06/30/2008, 2375, 26718.75,NULL
What I am trying to do is after the table is populated, I need to go back and calculate the EarnedIncome field to populate it.
the formula is DandA for the current month minus DandA for the previous month plus coupon.
Where I am having trouble is how can I do the update? So for 6/30 the value should be 4453.12 (2375-24640.63)+26718.75
I'll gladly take a clubbing over the head to get this resolved. thanks. Also, running under MS SQL2005 so any CTE ROW_OVER type solution can be used if possible.
You would need to use a subquery like this:
UPDATE #variabletable v1
SET EarnedIncome = DandA
- (SELECT DandA FROM #variabletable v2 WHERE GetMonthOnly(DATEADD(mm, -1, v2.EOMDate)=GetMonthOnly(v1.EOMDate))
+ Coupon
And I was making use of this helper function
DROP FUNCTION GetMonthOnly
GO
CREATE FUNCTION GetMonthOnly
(
#InputDate DATETIME
)
RETURNS DATETIME
BEGIN
RETURN CAST(CAST(YEAR(#InputDate) AS VARCHAR(4)) + '/' +
CAST(MONTH(#InputDate) AS VARCHAR(2)) + '/01' AS DATETIME)
END
GO
There's definitely quite a few ways to do this. You'll find pros and cons depending on how large your data set is, and other factors.
Here's my recommendation...
Declare #table as table
(
EOMDate DateTime,
DandA float,
Coupon Float,
EarnedIncome Float
)
Insert into #table Values('04/30/2008', 20187.5,17812.5,NULL)
Insert into #table Values('05/31/2008', 24640.63, 22265.63, NULL)
Insert into #table Values('06/30/2008', 2375, 26718.75,NULL)
--If we know that EOMDate will only contain one entry per month, and there's *always* one entry a month...
Update #Table Set
EarnedIncome=DandA-
(Select top 1 DandA
from #table t2
where t2.EOMDate<T1.EOMDate
order by EOMDate Desc)+Coupon
From #table T1
Select * from #table
--If there's a chance that there could be more per month, or we only want the values from the previous month (do nothing if it doesn't exist)
Update #Table Set
EarnedIncome=DAndA-(
Select top 1 DandA
From #table T2
Where DateDiff(month, T1.EOMDate, T2.EOMDate)=-1
Order by EOMDate Desc)+Coupon
From #Table T1
Select * from #table
--Leave the null, it's good for the data (since technically you cannot calculate it without a prior month).
I like the second method best because it will only calculate if there exists a record for the preceding month.
(add the following to the above script to see the difference)
--Add one for August
Insert into #table Values('08/30/2008', 2242, 22138.62,NULL)
Update #Table Set
EarnedIncome=DAndA-(
Select top 1 DandA
From #table T2
Where DateDiff(month, T1.EOMDate, T2.EOMDate)=-1
Order by EOMDate Desc
)+Coupon
From #Table T1
--August is Null because there's no july
Select * from #table
It's all a matter of exactly what do you want.
Use the record directly proceding the current record (regardless of date), or ONLY use the record that is a month before the current record.
Sorry about the format... Stackoverflow.com's answer editor and I do not play nice together.
:D
You can use a subquery to perform the calcuation, the only problem is what do you do with the first month because there is no previous DandA value. Here I've set it to 0 using isnull. The query looks like
Update MyTable
Set EarnedIncome = DandA + Coupon - IsNull( Select Top 1 DandA
From MyTable2
Where MyTable.EOMDate > MyTable2.EOMDate
Order by MyTable2.EOMDate desc), 0)
This also assumes that you only have one record per month in each table, and that there are't any gaps between months.
Another alternative is to calculate the running total when you are inserting your data, and have a constraint guarantee that your running total is correct:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
There may be a way to do this in a single statement, but in cases like this, I'd be inclined to set up a cursor to walk through each row, computing the new EarnedIncome field for that row, update the row, and then move to the next row.
Ex:
DECLARE #EOMDateVal DATETIME
DECLARE #EarnedIncomeVal FLOAT
DECLARE updCursor CURSOR FOR
SELECT EOMDate FROM #variabletable
OPEN updCursor
FETCH NEXT FROM updCursor INTO #EOMDateVal
WHILE ##FETCH_STATUS = 0
BEGIN
// Compute #EarnedIncomeVal for this row here.
// This also gives you a chance to catch data integrity problems
// that would cause you to fail the whole batch if you compute
// everything in a subquery.
UPDATE #variabletable SET EarnedIncome = #EarnedIncomeVal
WHERE EOMDate = #EOMDateVal
FETCH NEXT FROM updCursor INTO #EOMDateVal
END
CLOSE updCursor
DEALLOCATE updCursor