Find the latest payment value in between "valid" period - sql

It's quite hard for me to write nice title, so I'll try to describe the problem here in detail.
We have table [dbo].[LEASE_APPLICATIONS_AUDIT_LOG] that stores the historical data with changed datetimes. In case if the status of the data was NOT IN ('E', 'F', 'I', 'O', 'X') then that means that it was approved at that moment and if the status is other than these ones, then it is not approved. It can become approved and not approved in any order and many times. For example:
'2010.01.01', 'A'
'2010.02.01', 'B'
'2010.03.01', 'E'
'2010.04.01', 'Z'
That means that the record was approved from 2010.01.01 till 2010.03.01 and then was approved again at 2010.04.01.
There is another table [dbo].[LEASE_FINANCING_AUDIT_LOG] that stores the base payment amount that was at that time. For example if for the same record I would have such entries:
'2010.01.01', 123
'2010.04.01', 321
then that means that from base payment was 123 from 2010.01.01 till 2010.03.01 (became unapproved) and then from 2010.04.01 the value became to be 321.
There could be various combination of status changes and there could be different base payment values at any period of times.
So, the goal is to find the LATEST base_payment value that was in between APPROVED period.
Here are the scripts we've made so far. There are 2 tables with that data and the function. Other code snippets are taken from unit tests that's why they are like that and they will output 'bad' string if the logic is not as expected one. The function is working, but I do not really like the TOP 1/ORDER BY approach and trying to find if there is better way to achieve that. Any thoughts?
SET NOCOUNT ON;
CREATE TABLE [dbo].[LEASE_APPLICATIONS_AUDIT_LOG]
(
[LEASE_APPLICATION] CHAR(8)
, [APPLICATION_STATUS_CODE] CHAR(1)
, [CHANGED_DATE] DATETIME2(7) NOT NULL
);
GO
CREATE TABLE [dbo].[LEASE_FINANCING_AUDIT_LOG]
(
[LEASE_APPLICATION] CHAR(8)
, [BASE_PAYMENT] DECIMAL(10, 2) NULL
, [CHANGED_DATE] DATETIME2(7) NOT NULL
);
GO
CREATE FUNCTION [dbo].[post_approval_payment_amount] (#lease_application CHAR(8))
RETURNS TABLE
AS
RETURN (
SELECT TOP 1 lfal.BASE_PAYMENT AS post_approval_payment_amount
FROM LEASE_APPLICATIONS_AUDIT_LOG laal
CROSS APPLY
(
SELECT TOP 1 lf.BASE_PAYMENT
FROM LEASE_FINANCING_AUDIT_LOG lf
WHERE lf.LEASE_APPLICATION = laal.LEASE_APPLICATION
AND lf.CHANGED_DATE < COALESCE((
SELECT TOP 1 la.CHANGED_DATE
FROM LEASE_APPLICATIONS_AUDIT_LOG la
WHERE la.LEASE_APPLICATION = laal.LEASE_APPLICATION
AND la.CHANGED_DATE > laal.CHANGED_DATE
ORDER BY la.CHANGED_DATE
), CAST('9999-12-31 23:59:59' AS DATETIME))
ORDER BY lf.CHANGED_DATE DESC
) lfal
WHERE laal.LEASE_APPLICATION = #lease_application
AND laal.APPLICATION_STATUS_CODE NOT IN ('E', 'F', 'I', 'O', 'X')
ORDER BY laal.CHANGED_DATE DESC
);
GO
DECLARE #lease_application CHAR(8) = '35163328'
, #base_payment DECIMAL = 209.12
, #expected DECIMAL = 209.12
, #actual DECIMAL;
DECLARE #la AS TABLE
(
change_date DATETIME2(7)
, application_status_code CHAR(1) NULL
, base_amount DECIMAL NULL
, is_laal BIT
);
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'K', NULL, 1)
, ('2017-05-11 03:48:05.0600000', NULL, #base_payment, 0)
, ('2017-06-21 14:07:51.2200000', 'X', NULL, 1);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#expected <> #actual) SELECT 'Test 1 failed'
ELSE SELECT 'Test 1 passed';
SELECT #lease_application = '30000152'
, #base_payment = 622.15
, #expected = 622.15;
DELETE FROM #la;
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'z', NULL, 1)
, ('2017-05-11 03:48:05.0600000', NULL, #base_payment, 0);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#expected <> #actual) SELECT 'Test 2 failed'
ELSE SELECT 'Test 2 passed';
SELECT #lease_application = '38768578'
, #base_payment = 453.70
, #expected = NULL
, #actual = NULL;
DELETE FROM #la;
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
, ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
, ('2017-06-12 03:48:05.0600000', NULL, #base_payment, 0);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#actual IS NOT NULL) SELECT 'Test 3 failed'
ELSE SELECT 'Test 3 passed';
SELECT #lease_application = '38282661'
, #base_payment = 451.25
, #expected = 451.25;
DELETE FROM #la;
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'O', NULL, 1)
, ('2017-05-11 03:48:05.0600000', NULL, #base_payment, 0)
, ('2017-07-05 10:52:14.6800000', 'O', NULL, 1)
, ('2017-07-05 11:10:24.0400000', 'E', NULL, 1)
, ('2017-07-05 11:10:25.6000000', 'E', NULL, 1)
, ('2017-07-05 11:10:49.1900000', 'L', NULL, 1)
, ('2017-07-06 00:04:30.6400000', 'O', NULL, 1);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#expected <> #actual) SELECT 'Test 4 failed'
ELSE SELECT 'Test 4 passed';
SELECT #lease_application = '38768578'
, #base_payment = 453.70
, #expected = 453.70;
DELETE FROM #la;
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
, ('2017-05-11 03:48:05.0600000', NULL, 200, 0)
, ('2017-05-12 03:48:05.0600000', NULL, #base_payment, 0)
, ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
, ('2017-09-18 11:57:13.5100000', NULL, 100, 0);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#expected <> #actual) SELECT 'Test 5 failed'
ELSE SELECT 'Test 5 passed';
SELECT #lease_application = '38768578'
, #base_payment = 453.70
, #expected = 453.70;
DELETE FROM #la;
INSERT INTO #la ( change_date
, application_status_code
, base_amount
, is_laal
)
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
, ('2017-05-11 03:48:05.0600000', NULL, #base_payment, 0)
, ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
, ('2017-09-18 11:57:13.5100000', NULL, 100, 0);
INSERT INTO dbo.lease_applications_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, APPLICATION_STATUS_CODE
)
SELECT #lease_application
, l.change_date
, l.application_status_code
FROM #la AS l
WHERE l.is_laal = 1;
INSERT INTO dbo.lease_financing_audit_log ( LEASE_APPLICATION
, CHANGED_DATE
, BASE_PAYMENT
)
SELECT #lease_application
, l.change_date
, l.base_amount
FROM #la AS l
WHERE l.is_laal = 0;
SELECT #actual = post_approval_payment_amount
FROM [dbo].[post_approval_payment_amount](#lease_application);
IF (#expected <> #actual) SELECT 'Test 6 failed'
ELSE SELECT 'Test 6 passed';
DROP TABLE [dbo].[LEASE_APPLICATIONS_AUDIT_LOG];
GO
DROP TABLE [dbo].[LEASE_FINANCING_AUDIT_LOG];
GO
DROP FUNCTION [dbo].[post_approval_payment_amount];
GO

You asked for some thoughts about alternative approaches. You could use these ideas individually or in combination:
Remember that JOINs may be done using an operator other than the equal sign, so you could for example join your tables ON LEASE_APPLICATIONS_AUDIT_LOG.CHANGED_DATE <= LEASE_FINANCING_AUDIT_LOG.CHANGED_DATE.
The LAG and LEAD functions are highly optimal functions for retrieving "nearby" records. The third parameter even allows you to specify the value that will be returned in the event a record is not found - a convenient place to insert your CAST('9999-12-31 23:59:59' AS DATETIME default.
Never underestimate the clarity and performance that can be achieved by the proper use of Common Table Expressions (using the WITH clause).

Related

T-SQL how to filter by multiple criteria but prioritize what is returned?

I have the data below.
create table #results (
id int
, result_attr char(1)
, result varchar(100)
)
insert into
#results (id, result_attr, result)
values
(1, 'E', '***ERROR')
, (2, 'E', '***CORRECTED')
, (3, 'E', '***RESULTED')
, (4, 'E', '***AMENDED')
, (4, 'E', 'FOO')
, (5, 'E', 'ERROR***')
, (5, 'E', 'CORPOREAL')
, (6, 'E', '***CORRECTED')
, (7, 'E', '***RESULTED')
, (7, 'E', 'ABUNDANT')
, (7, 'E', 'PLENTITUDE')
, (8, 'E', 'INCORRECT')
, (9, 'A', 'HIGH')
, (10, 'A', 'LOW')
select *
from #results
drop table #results
The complete result set is:
My desired result set is:
This doesn't quite work:
select
res.id
, res.result_attr
, res.result
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E')
Tricky part being that I would want to exclude ID #4 with result "FOO" as well as ID #5 with result "CORPOREAL" and ID #7 with results "ABUNDANT" and "PLENTITUDE", but I want to keep ID #8 with result "INCORRECT". All in all, I want to exclude the following:
I've tried some windowing functions and other things, but am a bit stuck on this one. I would appreciate any assistance!
You can use row_number analytical function with conditional ordering as follows:
Select * from
(select
res.id
, res.result_attr
, res.result
, row_number() over (partition by res.id
order by case when charindex('***', res.result) > 0
then 1 else 2 end) as rn
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E') t
Where rn = 1
Order by id
Using:
select
res.id
, res.result_attr
, res.result
from results as res
where res.result LIKE '%***%' OR res.result = 'INCORRECT';
db<>fiddle demo

Duplicate in same row in different columns

i have a table like this:
CREATE TABLE #my_table (
intID int IDENTITY (1, 1),
num_1 varchar(100) NOT NULL,
num_2 varchar(100) NOT NULL,
num_3 varchar(100) NOT NULL,
num_4 varchar(100),
num_5 varchar(100),
isDuplicate char(1) DEFAULT 'N'
)
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'c', 'd', 'e')
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'c', 'd', 'e')
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'c', 'd', 'e')
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'a', 'd', 'e')
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'a', 'd', 'e')
INSERT INTO #my_table (num_1, num_2, num_3, num_4, num_5)
VALUES ('a', 'b', 'c', 'd', 'c')
I need to find duplicates in columns and get the row number which is duplicate.
my result should be
duplicate rows last 3 rows and is duplicate flag should be updated to 'Y'
This could also do the trick:
UPDATE #my_table
SET isDuplicate = 'Y'
WHERE intID IN (
SELECT intID FROM #my_table
WHERE EXISTS
(SELECT 1
FROM (VALUES
(num_1)
,(num_2)
,(num_3)
,(num_4)
,(num_5)) AS X (n)
WHERE NULLIF(n, '') IS NOT NULL
GROUP BY n
HAVING COUNT(*)>1
)
)
More information about table value constructors you can find here.
This should do the trick :
select num_1, num_2, num_3, count(*)
from #my_table
group by num_1, num_2, num_3
having count(*) > 1
Regards
This would set the duplicate column in the table to 'Y' if its a duplicate, you can the query from that
UPDATE #my_table
SET isDuplicate = 'Y'
WHERE intID IN
(
SELECT intID
FROM
(
SELECT intID, num_1, num_2, num_3,num_4, num_5,
RANK() OVER(PARTITION BY num_1, num_2, num_3, num_4, num_5 ORDER BY intID ASC) AS [rank]
FROM #my_table
) a
WHERE [rank] > 1
);
Try this:
UPDATE #my_table
SET isDuplicate =
CASE
WHEN
(select count(*)
from #my_table t2
where t2.intID <> #my_table.intID
and t2.num_1 = #my_table.num_1
and t2.num_2 = #my_table.num_2
and t2.num_3 = #my_table.num_3
and t2.num_4 = #my_table.num_4
and t2.num_5 = #my_table.num_5
) > 0 then 'Y'
ELSE 'N'
END

Not using Where statement in a context

Goal:
If you have the input data that is -10 then you should not use the WHERE statement in function.
Problem:
I do not know how to solve it in this context. You have to use WHERE and not WHERE depending on what input data you retrieve
Info:
If you use -10 as a input data then you should retrieve all data based on [dbo].[testing] and it is okay to retrieve data that is null in [dbo].[testing2] in relation to LEFT JOIN.
*The code and its data is a sample from production phase.
Thank you!
CREATE TABLE [dbo].[testing](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[testing2](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL,
[test_id] [int] NULL,
[id_type] [int] NOT NULL
) ON [PRIMARY]
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a left join [dbo].[testing2] b on a.id = b.[id]
where b.[id_type] = #data
)
INSERT INTO [test].[dbo].[testing] VALUES
(1, '', 2), (2, '', 3), (3, 'a', 2), (4, 'a', 2),
(5, 'b', 2), (6, 'b', 2), (7, 'c', 2), (8, 'c', 2),
(9, 'c', 2), (10, 'c', 2);
INSERT INTO [test].[dbo].[testing2] VALUES
(3, 'a' ,2 ,11 ,1), (4, 'a' ,2 ,11 ,1),
(5, 'a' ,2 ,11 ,0), (6, 'a' ,2 ,11 ,2);
select
s.[id],
s.[value],
s.[category],
s.[id_type]
from dbo.testt(1) s
Have your WHERE clause check if #data is either -10 or matches b.[id_type].
WHERE (#data = -10) OR (b.[id_type] = #data)
What about where b.[id_type] = #data OR #data = -10 in the testt function ?
So your function would be:
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a
LEFT JOIN [dbo].[testing2] b on a.id = b.[id]
WHERE b.[id_type] = #data OR #data = -10
)

Automate assignment queue with sql

I have a table that get updated every weeknight.
Someone then pull the table down, assigns values where the QueueT is null based on the number already assigned out and the type.
Think of it as a queueing system to even out workflow as evenly as possible.
Im stuck on attempting to automate the assignment piece
http://sqlfiddle.com/#!3/6a657/1
shows how much is assigned to each person, but how would i got about updating the table to assign each person based on type and even the assignments out as much as possible?
-- create temp table code if needed
create table #tempqueue
(
QueueT varchar(20)
,Type varchar(20)
)
insert into #tempqueue
(
QueueT
,Type
)
values
( 'bob' , 'type1'),
( 'bob' , 'type1'),
( 'john' , 'type2'),
( 'john' , 'type2'),
( 'john' , 'type2'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type2'),
( 'null' , 'type2'),
( 'tim' , 'type1'),
( 'bob' , 'type1'),
( 'jill' , 'type2'),
( 'jack' , 'type2'),
( 'john' , 'type2'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type1'),
( 'null' , 'type2'),
( 'null' , 'type2'),
( 'null' , 'type2')
select
QueueT
,type
,count(Type) counttype
from #tempqueue
group by
QueueT
,type
First, you need a unique id for the table so each record can be updated individually:
create table tempqueue
(
id int identity(1,1)
,QueueT varchar(20)
,Type varchar(20)
)
Then you can update it using a couple sub queries, min, count, and the row_number windowed function:
create table tempqueue
(
id int identity(1,1)
,QueueT varchar(20)
,Type varchar(20)
)
insert into tempqueue
(
QueueT
,Type
)
values
( 'bob' , 'type1'),
( 'bob' , 'type1'),
( 'john' , 'type2'),
( 'john' , 'type2'),
( 'john' , 'type2'),
( null , 'type1'),
( null , 'type1'),
( null , 'type3'),
( null , 'type1'),
( null , 'type2'),
( null , 'type2'),
( 'tim' , 'type1'),
( 'bob' , 'type1'),
( 'jill' , 'type2'),
( 'jack' , 'type2'),
( 'john' , 'type2'),
( null , 'type1'),
( null , 'type1'),
( null , 'type1'),
( null , 'type1'),
( null , 'type2'),
( null , 'type2'),
( null , 'type2')
-- loop through records until no records are updated
declare #rows_updated int = 1
while #rows_updated > 0
begin
update t
set t.QueueT = u.QueueT
from tempqueue t
-- get min record id per type where QueueT is null
inner join (select Type,min(id) id
from tempqueue
where QueueT is null
group by Type) id
on id.Type = t.Type
and id.id = t.id
-- get QueueT for each Type with the least count
inner join (select QueueT,Type,
row_number() over(partition by Type order by cnt) lst
from (select QueueT,Type,count(*) cnt
from tempqueue
where QueueT is not null
group by QueueT,Type)c)u
on u.Type = id.Type
and u.lst = 1
set #rows_updated = ##rowcount
end
select QueueT,Type,count(*) cnt
from tempqueue
group by QueueT,Type
SQL FIDDLE

how to select the min value using having key word

I have created the table stu_dep_det
CREATE TABLE `stu_dept_cs` (
`s_d_id` int(10) unsigned NOT NULL auto_increment,
`stu_name` varchar(15) , `gender` varchar(15) , `address` varchar(15),`reg_no` int(10) ,
`ex_no` varchar(10) ,
`mark1` varchar(10) ,
`mark2` varchar(15) ,
`mark3` varchar(15) ,
`total` varchar(15) ,
`avg` double(2,0),
PRIMARY KEY (`s_d_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC AUTO_INCREMENT=8 ;
then Inserted the values
INSERT INTO `stu_dept_cs` (`s_d_id`, `stu_name`, `gender`, `address`, `reg_no`, `ex_no`, `mark1`, `mark2`, `mark3`, `total`, `avg`) VALUES
(1, 'alex', 'm', 'chennai', 5001, 's1', '70', '90', '95', '255', 85),
(2, 'peter', 'm', 'chennai', 5002, 's1', '80', '70', '90', '240', 80),
(6, 'parv', 'f', 'mumbai', 5003, 's1', '88', '60', '80', '228', 76),
(7, 'basu', 'm', 'kolkatta', 5004, 's1', '85', '95', '56', '236', 79);
I want to select the min(avg) using having keyword and I have used the following sql statement
SELECT * FROM stu_dept_cs s having min(avg)
Is it correct or not plz write the correct ans....
select somecolumn1,somecolumn2
from stu_dept_cs
group by somecolumn1,somecolumn2,avg
having avg = min(avg)
or
with t1
(select rownumber() over (partition by somecolumn1,somecolumn2
order by somecolumn1,somecolumn2,avg asc) as rownum
from stu_dept_cs )
select * from t1 where rownum=1
SELECT t1.* FROM stu_dept_cs t1
LEFT JOIN stu_dept_cs t2
ON t1.avg > t2.avg
WHERE t2.stu_name IS NULL;