How to create a columns based on other columns SQL Server 2012 - sql-server

I have 2 tables #Claims and #ClaimsActivity:
Query:
declare #Claims table (ClaimID int)
insert into #Claims
values (6070), (6080)
declare #ClaimsActivity table
(
Activityid int,
ClaimID int,
Activity int,
ActivityDate datetime,
ClaimStatus int
)
insert into #ClaimsActivity
values (1, 6070, 0, '2017-11-05 20:23:16.640', 0),
(3, 6070, 6, '2017-11-06 13:50:28.203', 0),
(4, 6070, 9, '2017-11-07 13:39:28.410', 0),
(5, 6070, 10, '2017-11-07 13:40:49.980', 0),
(7, 6070, 8, '2017-11-07 15:46:18.367', 1),
(8, 6070, 8, '2017-11-07 16:50:49.543', 1),
(9, 6070, 9, '2017-11-07 16:50:54.733', 0),
(10, 6070, 4, '2017-11-07 16:55:22.135', 0),
(11, 6070, 6, '2017-11-08 18:32:15.101', 0),
(12, 6080, 0, '2017-11-12 11:15:17.199', 0),
(13, 6080, 8, '2017-11-13 09:12:23.203', 1)
select *
from #Claims
select *
from #ClaimsActivity
order by ActivityDate
I need to add 2 columns based on data in #ClaimsActivity: IsReopened and DateReopened
The logic is:
If the last ClaimStatus (based on ActivityDate) = 1 then IsReopened = 0
But if the last ClaimStatus = 0 then it need to go and check whether one of the Activity is = 9 (Claim Reopened)
and if one of the Activity = 9 then IsReopened should = 1 and DateReopened should be the last date when it was reopened
I brought column StatusOfClaim, but I also need IsReopened and DateReopened
select
Claimid,
isnull((select top 1
case when al.ClaimStatus = 1
then 'Closed'
else 'Open'
end
from
#ClaimsActivity al
where
C.ClaimID = al.ClaimID
order by
al.ActivityDate desc), 'Open') as 'Status of Claim',
NULL as 'isReopen',
NULL as 'DateReopened'
from
#Claims c
Desired output should be like this:

There are many different ways you can accomplish this, but here is an example using CROSS APPLY and OUTER APPLY:
SELECT
ClaimID,
CASE WHEN tmp.IsOpen = 1 THEN 'Open' ELSE 'Closed' END AS 'Status of Claim',
CASE WHEN tmp.IsOpen = 1 AND lastReopen.Activityid IS NOT NULL THEN 1 ELSE 0 END AS 'isReopen',
lastReopen.ActivityDate AS 'DateReopened'
FROM #Claims c
CROSS APPLY (
SELECT ISNULL((
SELECT TOP 1 CASE WHEN al.ClaimStatus = 1 THEN 0 ELSE 1 END
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID
ORDER BY al.ActivityDate DESC
), 1) AS IsOpen
) tmp
OUTER APPLY (
SELECT TOP 1
al.Activityid,
al.ActivityDate
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID AND al.Activity = 9
ORDER BY al.ActivityDate DESC
) lastReopen
The CROSS APPLY is just used to produce a column that tells us whether a claim is open or closed, and we can reuse this throughout the rest of the query.
The OUTER APPLY is used to grab to the last "reopen" activity for each claim, of which you want the date.
I can't attest to the performance of this query, but this should at least give you the correct results.

Related

SQL Server : I am trying to add a conditional select of column values based on another column

Here is my example table:
CREATE TABLE demo1
(
ProviderID int NOT NULL,
ProviderClientID int NOT NULL,
ClientIdentifier nvarchar(9) NOT NULL,
ClientIdentifierTypeID int NOT NULL
);
INSERT INTO demo1 (ProviderID, ProviderClientID, ClientIdentifier, ClientIdentifierTypeID)
VALUES
(5, 7, 123444567, 1),
(5, 7, 000111234, 2),
(5, 11, 145342332, 1),
(5, 12, 234212345, 1),
(5, 13, 324564332, 3),
(5, 14, 123222467, 3),
(5, 19, 234444879, 1),
(5, 19, 000111643, 2),
(5, 19, 999234252, 3),
(5, 20, 456333245, 1)
The column ClientIdentifierTypeID values mean:
1 - Drivers License #,
2 - SchoolID,
3 - StateID
So clients can be in the table multiple times with different identifier types.
I want to select each client & just their drivers license #. If they don't have drivers license, then get their state id. I don't want their school id at all.
This is going to be part of a client count I am going to do next.
Here is what I tried in my query so far (I filter out school id already):
WITH A AS
(
SELECT
*
FROM
demo1
WHERE
ClientIdentifierTypeID IN ('1', '3')
), B AS
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY [ProviderClientID] ORDER BY [ClientIdentifierTypeID]) AS rn
FROM
A
)
SELECT
*
FROM
B
WHERE
rn = 1
EDIT: I am re-reading through my query, it seems do what I want.
When there is a license# rn will be 1. If no license, the state id will have rn=1.
If counts clients including school id then disable where clause otherwise enable it.
-- sql server
SELECT ProviderClientID
, COUNT(1) count_client
, MIN(CASE WHEN ClientIdentifierTypeID = 1
THEN ClientIdentifier
WHEN ClientIdentifierTypeID = 3
THEN ClientIdentifier
END)
FROM demo1
-- WHERE ClientIdentifierTypeID IN ('1', '3')
GROUP BY ProviderClientID;
I use two Selects to filter. One is to get the uses who has driver License . Second block is to get the users who has Sate but not driver license.
then combine both lists with UNION ALL.
This is one possibility.
--client has driver License
SELECT * FROM demo1
WHERE ClientIdentifierTypeID in (1)
AND ClientIdentifierTypeID not in (3,2)
UNION ALL
-- Clients who has State and not driver License
SELECT * FROM demo1
WHERE ClientIdentifierTypeID in (3)
and ProviderClientID Not in (SELECT ProviderClientID FROM demo1
WHERE ClientIdentifierTypeID in (1)
)

Sort results based on order of entries in the WHERE's IN clause

I have a query
select *
from qtable
where qid in (15,555,2,3,4,5,36,27,18,9)
here qid is the primary key.
I get the following results sorted out by qid as it is the primary key but my requirement is to get the results as mentioned in the in (15, 555, 2, 3, 4, 5, 36, 27, 18, 9) statement.
Is there a way to unsort the records?
select qtable.*
from qtable join (values (15),(55),(2),(3),(4))a(id)
on a.Id = qtable.qID
SQL Server and other ANSI SQL compliant database require an ORDER BY clause to return data in a particular order. One approach is to use a row constructor that includes the desired sequence:
SELECT qtable.*
FROM qtable
JOIN (VALUES
(1, 15)
,(2, 555)
,(3, 2)
,(4, 3)
,(5, 4)
,(6, 5)
,(7, 36)
,(8, 27)
,(9, 18)
,(10, 9)
) AS list(seq, value) ON qtable.quid = list.qid
ORDER BY list.seq;
A similar technique can be used for variable list of values passed as a table-valued parameter or table variable:
DECLARE #list TABLE (
seq int
, qid int);
INSERT INTO #list VALUES
(1, 15)
,(2, 555)
,(3, 2)
,(4, 3)
,(5, 4)
,(6, 5)
,(7, 36)
,(8, 27)
,(9, 18)
,(10, 9);
SELECT qtable.*
FROM qtable
JOIN #list AS list ON qtable.quid = list.qid
ORDER BY list.seq;
You can do it like this:
select qid,case when qid = 15 then 1
when qid = 555 then 2
when qid = 2 then 3
when qid = 3 then 4
when qid = 4 then 5
when qid = 5 then 6
when qid = 36 then 7
when qid = 27 then 8
when qid = 18 then 9
when qid = 9 then 10
end as qidflag
from qtable where qid in (15,555,2,3,4,5,36,27,18,9)
order by qidflag
Here's an option where you can pass the desired ID/Sequence as a delimited string.
The Sequence will be maintained.
Example
Declare #List varchar(max)='15,555,2,3,4,5,36,27,18,9'
Select A.*
From qtable A
Join (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = v.value('(./text())[1]', 'int')
From (values (convert(xml,'<x>' + replace(#List,',','</x><x>')+'</x>'))) x(n)
Cross Apply n.nodes('x') node(v)
) B on A.quid = B.RetVal
Order By B.RetSeq
If it Helps - the sub-query Returns
RetSeq RetVal
1 15
2 555
3 2
4 3
5 4
6 5
7 36
8 27
9 18
10 9
I would use something similar to the following:
DECLARE #qidList TABLE (sort INT NOT NULL IDENTITY(1,1), qid INT);
INSERT #qidList VALUES
(15),
(555),
(2),
(3),
(4),
(5),
(36),
(27),
(18),
(9);
select qtable.*
from qtable
join #qidList list on list.qid=qtable.qid
order by list.sort;

How to write a case when statement when there are overlaps in T-SQL

I have a table like this
How can I group it to this
Small is the sum of the count when Count <25; Large is the sum of the count when Count>=25; Total is the sum of all counts.
Try it like this...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
ID INT NOT NULL PRIMARY KEY,
nCount int NOT NULL
);
INSERT #TestData (ID, nCount) VALUES
(1, 10), (2, 15), (3, 22), (4, 23),
(5, 25), (6, 27), (7, 30);
--=====================================
WITH
cte_Totals AS (
SELECT
Total = SUM(td.nCount),
Small = SUM(CASE WHEN td.nCount < 25 THEN td.nCount ELSE 0 END),
Large = SUM(CASE WHEN td.nCount >= 25 THEN td.nCount ELSE 0 END)
FROM
#TestData td
)
SELECT
x.[Group],
x.[Count]
FROM
cte_Totals t
CROSS APPLY (VALUES (1, 'Total', t.Total), (2, 'Small', t.Small), (3, 'Large', t.Large) ) x (SortBy, [Group],[Count])
ORDER BY
x.SortBy;
Results...
Group Count
----- -----------
Total 152
Small 70
Large 82
HTH,
Jason
The simplest way is to use CASE:
SELECT
SUM(Count) as Total,
SUM(CASE WHEN Count < 25 THEN Count ELSE 0 END) as Small,
SUM(CASE WHEN Count >= 25 THEN Count ELSE 0 END) as Large
FROM table
Late answer (keep the accepted as is), but I did want to introduce a concept which may be more helpful down the line.
I maintain a generic Tier Table. The following is a simplified example, but you can take the aggregation tiers out of the code, and put it in a table... things change, and you can serve multiple masters.
Sample Data
Declare #YourTable table (ID int,[Count] int)
Insert Into #YourTable values
(1, 10), (2, 15), (3, 22), (4, 23), (5, 25), (6, 27), (7, 30)
Declare #Tier table (Tier varchar(50),Seq int,Title varchar(50),R1 int,R2 int)
Insert Into #Tier values
('MyGroup',1,'Total',0,99999)
,('MyGroup',2,'Small',0,25)
,('MyGroup',3,'Large',25,99999)
The Actual Query
Select T.Title
,[Count] = sum(D.[Count])
From #Tier T
Join #YourTable D on (T.Tier='MyGroup' and D.Count >= T.R1 and D.Count<T.R2)
Group By T.Title,T.Seq
Order By T.Seq
Returns
Title Count
Total 152
Small 70
Large 82
EDIT - There are many ways you can construct this
Example
Declare #YourTable table (ID varchar(50),[Count] int)
Insert Into #YourTable values
('Tywin', 10), ('Tywin', 15), ('Tyrion', 22), ('Bran', 23), ('Ned', 25), ('John', 27), ('Robb', 30)
Declare #Tier table (Tier varchar(50),Seq int,Title varchar(50),R1 int,R2 int,C1 varchar(50),C2 varchar(50))
Insert Into #Tier values
('MyGroup',1,'Total' ,null,null,'a','z')
,('MyGroup',2,'Group 1',null,null,'Tywin,Tyrion',null)
,('MyGroup',3,'Group 2',null,null,'Bran,Ned,John,Robb',null)
Select T.Title
,[Count] = sum(D.[Count])
From #Tier T
Join #YourTable D on T.Tier='MyGroup' and (D.ID between C1 and C2 or patindex('%,'+D.ID+',%',','+C1+',')>0)
Group By T.Title,T.Seq
Order By T.Seq
Returns
Title Count
Total 152
Group 1 47
Group 2 105

How do you validate that range doesn't overlap in a list of data?

I have a list of data :
Id StartAge EndAge Amount
1 0 2 50
2 2 5 100
3 5 10 150
4 6 9 160
I have to set Amount for various age group.
The age group >0 and <=2 need to pay 50
The age group >2 and <=5 need to pay 100
The age group >5 and <=10 need to pay 150
But
The age group >6 and <=9 need to pay 160 is an invalid input because >6 and <=9 already exist on 150 amount range.
I have to validate such kind of invalid input before inserting my data as a bulk.Once 5-10 range gets inserted anything that is within this range should not be accepted by system. For example: In above list, user should be allowed to insert 10-15 age group but any of the following should be checked as invalid.
6-9
6-11
3-5
5-7
If Invalid Input exists on my list I don't need to insert the list.
You could try to insert your data to the temporary table first.
DECLARE #TempData TABLE
(
[Id] TINYINT
,[StartAge] TINYINT
,[EndAge] TINYINT
,[Amount] TINYINT
);
INSERT INTO #TempData ([Id], [StartAge], [EndAge], [Amount])
VALUES (1, 0, 2, 50)
,(2, 2, 5, 100)
,(3, 5, 10, 150)
,(4, 6, 9, 160);
Then, this data will be transferred to your target table using INSERT INTO... SELECT... statement.
INSERT INTO <your target table>
SELECT * FROM #TempData s
WHERE
NOT EXISTS (
SELECT 1
FROM #TempData t
WHERE
t.[Id] < s.[Id]
AND s.[StartAge] < t.[EndAge]
AND s.[EndAge] > t.[StartAge]
);
I've created a demo here
We can use recursive CTE to find how records are chained by end age and start age pairs:
DECLARE #DataSource TABLE
(
[Id] TINYINT
,[StartAge] TINYINT
,[EndAge] TINYINT
,[Amount] TINYINT
);
INSERT INTO #DataSource ([Id], [StartAge], [EndAge], [Amount])
VALUES (1, 0, 2, 50)
,(2, 2, 5, 100)
,(3, 5, 10, 150)
,(4, 6, 9, 160)
,(5, 6, 11, 160)
,(6, 3, 5, 160)
,(7, 5, 7, 160)
,(9, 10, 15, 20)
,(8, 7, 15, 20);
WITH PreDataSource AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY [StartAge] ORDER BY [id]) as [pos]
FROM #DataSource
), DataSource AS
(
SELECT [Id], [StartAge], [EndAge], [Amount], [pos]
FROM PreDataSource
WHERE [id] = 1
UNION ALL
SELECT R.[Id], R.[StartAge], R.[EndAge], R.[Amount], R.[pos]
FROM DataSource A
INNER JOIN PreDataSource R
ON A.[Id] < R.[Id]
AND A.[EndAge] = R.[StartAge]
AND R.[pos] =1
)
SELECT [Id], [StartAge], [EndAge], [Amount]
FROM DataSource;
This is giving us, the following output:
Note, that before this, we are using the following statement to prepare the data:
SELECT *, ROW_NUMBER() OVER (PARTITION BY [StartAge] ORDER BY [id]) as [pos]
FROM #DataSource;
The idea is to find records with same start age and to calculated which one is inserted first. Then, in the CTE we are getting only the first.
Assuming you are bulk inserting the mentioned data into a temp table(#tmp) or table variable (#tmp).
If you are working on sql server 2012 try the below.
select *
from(select *,lag(endage,1,0)over(order by endage) as [col1]
from #tmp)tmp
where startage>=col1 and endage>col1
The result of this query should be inserted into your main table.

Select query using variable not running in mssql

Select query is not working when use variable in MSSQL2014
My Schema is :-
CREATE TABLE product
(idproduct int, name varchar(50), description varchar(50), tax decimal(18,0))
INSERT INTO product
(idproduct, name, description,tax)
VALUES
(1, 'abc', 'This is abc',10),
(2, 'xyz', 'This is xyz',20),
(3, 'pqr', 'This is pqr',15)
CREATE TABLE product_storage
(idstorage int,idproduct int,added datetime, quantity int, price decimal(18,0))
INSERT INTO product_storage
(idstorage,idproduct, added, quantity,price)
VALUES
(1, 1, 2010-01-01,0,10.0),
(2, 1, 2010-01-02,0,11.0),
(3, 1, 2010-01-03,10,12.0),
(4, 2, 2010-01-04,0,12.0),
(5, 2, 2010-01-05,10,11.0),
(6, 2, 2010-01-06,10,13.0),
(7, 3, 2010-01-07,10,14.0),
(8, 3, 2010-01-07,10,16.0),
(9, 3, 2010-01-09,10,13.0)
and i am executing below command:-
declare #price1 varchar(10)
SELECT p.idproduct, p.name, p.tax,
[#price1]=(SELECT top 1 s.price
FROM product_storage s
WHERE s.idproduct=p.idproduct AND s.quantity > 0
ORDER BY s.added ASC),
(#price1 * (1 + tax/100)) AS [price_with_tax]
FROM product p
;
This is not working in MSSQL, Please Help me out.
for detail check http://sqlfiddle.com/#!6/91ec2/296
And My query is working in MYSQL
Check for detail :- http://sqlfiddle.com/#!9/a71b8/1
Try this query
SELECT
p.idproduct
, p.name
, p.tax
, (t1.price * (1 + tax/100)) AS [price_with_tax]
FROM product p
inner join
(
SELECT ROW_NUMBER() over (PARTITION by s.idproduct order by s.added ASC) as linha, s.idproduct, s.price
FROM product_storage s
WHERE s.quantity > 0
) as t1
on t1.idproduct = p.idproduct and t1.linha = 1
Try it like this:
Explanantion: You cannot use a variable "on the fly", but you can do row-by-row calculation in an APPLY...
SELECT p.idproduct, p.name, p.tax,
Price.price1,
(price1 * (1 + tax/100)) AS [price_with_tax]
FROM product p
CROSS APPLY (SELECT top 1 s.price
FROM product_storage s
WHERE s.idproduct=p.idproduct AND s.quantity > 0
ORDER BY s.added ASC) AS Price(price1)
;
EDIT: Your Fiddle uses a bad literal date format, try this:
INSERT INTO product_storage
(idstorage,idproduct, added, quantity,price)
VALUES
(1, 1, '20100101',0,10.0),
(2, 1, '20100102',0,11.0),
(3, 1, '20100103',10,12.0),
(4, 2, '20100104',0,12.0),
(5, 2, '20100105',10,11.0),
(6, 2, '20100106',10,13.0),
(7, 3, '20100107',10,14.0),
(8, 3, '20100108',10,16.0),
(9, 3, '20100109',10,13.0)
Here is the correct schema for SQL Server and query runs perfect as Shnugo Replied.
VALUES
(1, 1, convert(datetime,'2010-01-01'),0,10.0),
(2, 1, convert(datetime,'2010-01-02'),0,11.0),
(3, 1, convert(datetime,'2010-01-03'),10,12.0),
(4, 2, convert(datetime,'2010-01-04'),0,12.0),
(5, 2, convert(datetime,'2010-01-05'),10,11.0),
(6, 2, convert(datetime,'2010-01-06'),10,13.0),
(7, 3, convert(datetime,'2010-01-07'),10,14.0),
(8, 3, convert(datetime,'2010-01-07'),10,16.0),
(9, 3, convert(datetime,'2010-01-09'),10,13.0)

Resources