Last record per transaction - sql

I am trying to select the last record per sales order.
My query is simple in SQL Server management.
SELECT *
FROM DOCSTATUS
The problem is that this database has tens of thousands of records, as it tracks all SO steps.
ID SO SL Status Reason Attach Name Name Systemdate
22 951581 3 Processed Customer NULL NULL BW 2016-12-05 13:33:27.857
23 951581 3 Submitted Customer NULL NULL BW 2016-17-05 13:33:27.997
24 947318 1 Hold Customer NULL NULL bw 2016-12-05 13:54:27.173
25 947318 1 Invoices Submit Customer NULL NULL bw 2016-13-05 13:54:27.300
26 947318 1 Ship Customer NULL NULL bw 2016-14-05 13:54:27.440
I would to see the most recent record per the SO
ID SO SL Status Reason Attach Name Name Systemdate
23 951581 4 Submitted Customer NULL NULL BW 2016-17-05 13:33:27.997
26 947318 1 Ship Customer NULL NULL bw 2016-14-05 13:54:27.440

Well I'm not sure how that table has two Name columns, but one easy way to do this is with ROW_NUMBER():
;WITH cte AS
(
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY SO ORDER BY Systemdate DESC)
FROM dbo.DOCSTATUS
)
SELECT ID, SO, SL, Status, Reason, ..., Systemdate
FROM cte WHERE rn = 1;
Also please always reference the schema, even if today everything is under dbo.

I think you can keep it this simple:
SELECT *
FROM DOCSTATUS
WHERE ID IN (SELECT MAX(ID)
FROM DOCSTATUS
GROUP BY SO)
You want only the maximum ID from each SO.

An efficient method with the right index is a correlated subquery:
select t.*
from t
where t.systemdate = (select max(t2.systemdate) from t t2 where t2.so = t.so);
The index is on (so, systemdate).

Related

deleting specific duplicate and original entries in a table based on date

i have a table called "main" which has 4 columns, ID, name, DateID and Sign.
i want to create a query that will delete entries in this table if there is the same ID record in twice within a certain DateID.
i have my where clause that searches the previous 3 weeks
where DateID =((SELECT MAX( DateID)
WHERE DateID < ( SELECT MAX( DateID )-3))
e.g of my dataset im working with:
id
name
DateID
sign
12345
Paul
1915
Up
23658
Danny
1915
Down
37868
Jake
1916
Up
37542
Elle
1917
Up
12345
Paul
1917
Down
87456
John
1918
Up
78563
Luke
1919
Up
23658
Danny
1920
Up
in the case above, both entries for ID 12345 would need to be removed.
however the entries for ID 23658 would need to be kept as the DateID > 3
how would this be possible?
You can use window functions for this.
It's not quite clear, but it seems LAG and conditional COUNT should fit what you need.
DELETE t
FROM (
SELECT *,
CountWithinDate = COUNT(CASE WHEN t.PrevDate >= t.DateId - 3 THEN 1 END) OVER (PARTITION BY t.id)
FROM (
SELECT *,
PrevDate = LAG(t.DateID) OVER (PARTITION BY t.id ORDER BY t.DateID)
FROM YourTable t
) t
) t
WHERE CountWithinDate > 0;
db<>fiddle
Note that you do not need to re-join the table, you can delete directly from the t derived table.
Hope this works:
DELETE FROM test_tbl
WHERE id IN (
SELECT T1.id
FROM test_tbl T1
WHERE EXISTS (SELECT 1 FROM test_tbl T2 WHERE T1.id = T2.id AND ABS(T2.dateid - T1.dateid) < 3 AND T1.dateid <> T2.dateid)
)
In case you need more logic for data processing, I would suggest using Stored Procedure.

Running Balance/Total on 2 columns

I have the following Table in Azure MSSQL:
ID,
Charge,
Payment
ID is the Primary Key and unique, Charge and Payment are Numeric.
ID charge payment
1 10 null
2 null 10
3 40 null
4 null 30
I want to do the following query:
select *,
SUM(charge)-sum(payment) OVER (order by id) AS Balance
from Table T
order by id asc
Which in the above data sample would look like this:
ID charge payment balance
1 10 null 10
2 null 10 0
3 40 null 40
4 null 30 10
However that query fails, complaining I need to do add a Group by clause, however if I run the following
select *,
SUM(charged) OVER (order by id) AS totalCharged
from table
order by id
That works fine - I feel like I've missed something obvious.
I should also note there are other columns that are in the final query but are omitted from here since they aren't relevant to the logic.
As it turns out, I was missing something obvious.
I changed the source query to use 0 instead of Null and then did the following:
select *,
SUM(charged - payment) OVER (order by id) AS balance
from table
order by id
So figured it out myself whilst thinking upon the porcelein throne.

SQL aggregate rows with same id , specific value in secondary column

I'm looking to filter out rows in the database (PostgreSQL) if one of the values in the status column occurs. The idea is to sum the amount column if the unique reference only has a status equals to 1. The query should not SELECT the reference at all if it has also a status of 2 or any other status for that matter. status refers to the state of the transaction.
Current data table:
reference | amount | status
1 100 1
2 120 1
2 -120 2
3 200 1
3 -200 2
4 450 1
Result:
amount | status
550 1
I've simplified the data example but I think it gives a good idea of what I'm looking for.
I'm unsuccessful in selecting only references that only have status 1.
I've tried sub-queries, using the HAVING clause and other methods without success.
Thanks
Here's a way using not exists to sum all rows where the status is 1 and other rows with the same reference and a non 1 status do not exist.
select sum(amount) from mytable t1
where status = 1
and not exists (
select 1 from mytable t2
where t2.reference = t1.reference
and t2.status <> 1
)
SELECT SUM(amount)
FROM table
WHERE reference NOT IN (
SELECT reference
FROM table
WHERE status<>1
)
The subquery SELECTs all references that must be excluded, then the main query sums everything except them
select sum (amount) as amount
from (
select sum(amount) as amount
from t
group by reference
having not bool_or(status <> 1)
) s;
amount
--------
550
You could use windowed functions to count occurences of status different than 1 per each group:
SELECT SUM(amount) AS amount
FROM (SELECT *,COUNT(*) FILTER(WHERE status<>1) OVER(PARTITION BY reference) cnt
FROM tc) AS sub
WHERE cnt = 0;
Rextester Demo

update in oracle sql : multiple rows in 1 table

I am new to SQL and I am no good with more advanced queries and functions.
So, I have this 1 table with sales:
id date seller_name buyer_name
---- ------------ ------------- ------------
1 2015-02-02 null Adrian
1 2013-05-02 null John B
1 2007-11-15 null Chris F
2 2014-07-12 null Jane A
2 2011-06-05 null Ted D
2 2010-08-22 null Maryanne A
3 2015-12-02 null Don P
3 2012-11-07 null Chris T
3 2011-10-02 null James O
I would like to update the seller_name for each id, by putting the buyer_name from previous sale as seller_name to newer sale date. For example, for on id 1 John B would then be seller in 2015-02-02 and buyer in 2013-05-02. Does that make sense?
P.S. This is the perfect case, the table is big and the ids are not ordered so neat.
merge into your_table a
using ( select rowid rid,
lead(buyer_name, 1) over (partition by id order by date desc) seller
from your_table
) b
on (a.rowid = b.rid )
when matched then update set a.seller_name= b.seller;
Explanation : Merge into statement performs different operations based on matched or not matched criterias. Here you have to merge into your table, in the using having the new values that you want to take and also the rowid which will be your matching key. The lead function gets the result from the next n rows depending on what number you specify after the comma. After specifying how many rows to jump you also specify on what part to work, which in your case is partitioned by id and ordered by date so you can get the seller, who was the previous buyer. Hope this clears it up a bit.
Either of the below query can be used to perform the desire action
merge into sandeep24nov16_2 table1
using(select rowid r, lag(buyer_name) over (partition by id order by "DATE" asc) update_value from sandeep24nov16_2 ) table2
on (table1.rowid=table2.r)
when matched then update set table1.seller_name=table2.update_value;
or
merge into sandeep24nov16_2 table1
using(select rowid r, lead(buyer_name) over (partition by id order by "DATE" desc) update_value from sandeep24nov16_2 ) table2
on (table1.rowid=table2.r)
when matched then update set table1.seller_name=table2.update_value;
select a.*,
lag(buyer_name, 1) over(partition by id order by sale_date) seller_name
from <your_table> a;

SQL view with a column that shows top result of relationship with multiple weightings

I have three tables, an Objects table, a Status table and a StatusTypes Table.
An Object has Multiple Status' which each has a status type. I would like to create a view that gives me the objects ID, and Most Important Status Description which is found in the StatusTypes table, and the most important status Date which is in the Status Table.
The part I am getting hung up on is to find the most Important Status It must first be sorted by the latest date, then by a integer weighting (Priority) in the Status Table then again by another weighting in the StatusTypes Table (Weighting)
What would be the best SQL statement to quickly deliver these results.
Objects
ID Aquisiton Date Serial Number
127237 1997-04-21 2151513515
127239 1997-10-31 2151513523
127242 1998-01-20 2165588481
127272 1998-10-20 2195689842
127286 1999-06-15 2231549489
127291 1999-06-01 2229564978
Status
ID ObjectID Priority StatusMessage Date Status
1 127237 1 Online 22.02.12 07.01.00 1
2 127237 3 Job Received 22.02.12 07.01.00 3
3 127237 5 Job Started 22.02.12.07.01.00 3
4 127237 5 Jam 22.02.12.07.01.00 2
5 127286 1 Online 22.02.12.07.09.00 1
Status Types
ID Description Weighting
1 Idle 0
2 Error 9
3 Working 5
Expected Output##
ID Status Date
127237 Error 22.02.12 07.01.00
127286 Idle 22.02.12.07.09.00
Sounds like you could use ROW_NUMBER():
SELECT *
FROM (SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Date DESC, Priority, Weighting) 'RowRank'
FROM YourTable a
)sub
WHERE RowRank = 1
Obviously replacing YourTable with the relevant JOIN's
The ROW_NUMBER() function assigns a number to each row. PARTITION BY is optional, but used to start the numbering over for each value in that group, ie: if you PARTITION BY ID then for each unique ID value the numbering will start over at 1. ORDER BY of course is used to define how the counting should go, and is required in the ROW_NUMBER() function.
Updated with your data:
SELECT ObjectID,Description,Date
FROM (SELECT a.*,b.Description,ROW_NUMBER() OVER(PARTITION BY a.ObjectID ORDER BY CONVERT(DATE,LEFT([Date],8),4) DESC, Priority DESC, Weighting DESC) 'RowRank'
FROM Status a
JOIN Status_Types b
ON a.Status = b.ID
)sub
WHERE RowRank = 1
Demo: SQL Fiddle