Conditionally convert a decimal value to integer - sql

create table #Temp
(
id int,
Volume decimal(18,3)
)
insert into #Temp(id,Volume)values(1,10)
insert into #Temp(id,Volume)values(2,10.2)
id Volume
-----------------
1 10.000
2 10.200
Declare #Type as int
set #Type=1
select Id,Convert(varchar(10),CASE WHEN #Type=1 THEN CAST(Volume AS INT)
ELSE Volume END) AS Quantity from #Temp
It is showing a result like this
id Volume
-----------------
1 10.000
2 10.000
But I want result like this when type is 1 then result should be in integer format:
id Volume
-----------------
1 10
2 10
on else condition (#Type rather than 1) I want result like this
id Volume
-----------------
1 10.000
2 10.200
I have tried this query
select Id,Convert(varchar(10),CASE WHEN #Type=1 THEN CAST(Volume AS INT)
ELSE Volume END) AS Quantity from #Temp

This will work for you.
select Id, (CASE WHEN #Type=1 THEN convert(varchar(10),convert(int,volume))
ELSE convert(varchar(10),Volume) END) AS Quantity from #Temp
When #type is 1, the result will be 10 for both values. Otherwise it will be exact value in the table.

Related

SQL Server Subtract a value from records in the order of first to last

I have a table temp with 2 columns
create table temp
(
id int identity(1,1),
amount decimal(18,2)
)
Sample data insert as follows
insert into temp(amount)
values (100), (200), (500)
Table will look like
id amount
-----------
1 100
2 200
3 500
What I am trying to achieve is if suppose we deduct an amount of 150 from the table then deduction should happen in the order of Id. Which means the amount of id 1 will be 0 (100-150 =0 and remaining is 50) and then amount of id 2 will be 150 (balance 50 from previous deduction must be reduced from 200)
So the result data set should be
id amount
---------
1 0
2 150
3 500
Window cumulative summation for all previous records, save to cte for aliasing:
create table #a(id int,amount int)
insert #a values(1,100),(2,200),(3,500)
select * from #a
declare #sub int=150
;with cte as
(
select id,amount,isnull(sum(amount) over (order by id rows between unbounded preceding and 1 preceding),0) as prev_sum
from #a
)
select *,case
when #sub-prev_sum>amount then 0
when #sub-prev_sum>0 then amount-(#sub-prev_sum)
else amount
end
from cte

SQL COUNT the number of records on a certain date

I trying to create a SQL query/stored procedure to count the number of applications in-progress on a certain day for a certain team.
Where I am having trouble is the following scenario: when the application is transferred to another user, the count for the day should not double count (a count for each team on the day of transfer) and should go to the transferred user.
My Tables
**Users**
Id || Name || TeamId
---------------------------------
1 User 1 1
2 User 2 2
**Application**
Id || Name
-------------
1 Application1
**ApplicationUser**
Id || ApplicationId || UserId || AssignedDate || UnassignedDate
----------------------------------------------------------
1 1 1 2018-03-01 2018-03-02
2 1 2 2018-03-02 2018-03-03
so in the Stored Procedure I am sending a date in as a parameter and the result i want to return is the following.
Date || Team 1 || Team 2 || Total
-------------------------------------------
2018-03-02 0 1 1
so if I put all results together they would look like this.
Date || Team 1 || Team 2 || Total
-------------------------------------------
2018-02-28 0 0 0
2018-03-01 1 0 1
2018-03-02 0 1 1
2018-03-03 0 1 1
Thank you very much in advance :)
Try this:
DDL:
Here I define table variables and insert data you provided:
declare #users table (id int, name varchar(10), teamid int)
insert into #users values (1, 'user1', 1),(2, 'user2',2)
declare #application table (id int, name varchar(15))
insert into #application values (1, 'application1')
declare #applicationuser table (id int, applicationid int, userid int, assigneddate date, unassigneddate date)
insert into #applicationuser values (1,1,1,'2018-03-01','2018-03-02'),(2,1,2,'2018-03-02','2018-03-03')
Query:
--here I sum values for each team using cumulative sum
select [date],
sum(team1) over (order by [date] rows between unbounded preceding and current row) [team1],
sum(team2) over (order by [date] rows between unbounded preceding and current row) [team2],
sum(total) over (order by [date] rows between unbounded preceding and current row) [total]
from (
--here I am pivoting inner query, replacing NULL values using COALESCE
select [date],
coalesce(max(case when teamid = 1 then value end), 0) [team1],
coalesce(max(case when teamid = 2 then value end), 0) [team2],
coalesce(max(case when teamid = 1 then value end), 0) + coalesce(max(case when teamid = 2 then value end), 0) [total]
from (
--here I join assigned and unassigned dates with team ids
select [AU].assigneddate [date], [U].teamid, 1 [value] from #applicationuser [AU]
join #users [U] on [AU].userid = [U].id
union all
select [AU].unassigneddate, [U].teamid, -1 from #applicationuser [AU]
join #users [U] on [AU].userid = [U].id
) a
group by [date]
) a
In order to understand this better is to try every query separatly, i.e. execute most inner query first, then wrap it with outer query and see the results. This way you'll know what's happening at each step.
These kind of queries are best dealt with using a dates table. If you don't have one in your database already I strongly suggest you get one.
In the meanwhile, you can create a dates table on the fly using either an inline table valued function or a derived table within your query (Note I have added an additional open application):
-- Declare test data
declare #users table (id int, name varchar(10), teamid int);
declare #application table (id int, name varchar(15));
declare #applicationuser table (id int, applicationid int, userid int, assigneddate date, unassigneddate date);
insert into #users values (1, 'user1', 1),(2, 'user2',2);
insert into #application values (1, 'application1'),(2, 'application1');
insert into #applicationuser values (1,1,1,'2018-03-01','2018-03-02'),(2,1,2,'2018-03-02','2018-03-03'),(2,2,2,'2018-03-02','2018-03-05');
-- Find the maximum date range possible to create a dates table that covers all possible application periods
declare #MinDate date = (select min(AssignedDate) from #applicationuser);
declare #MaxDate date = (select max(UnassignedDate) from #applicationuser);
-- This is a derived table that simply returns 10 rows
with t(t) as(select * from(values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(t))
-- This then CROSS JOINs the 10 rows to create 10*10*10*10*10 = 100,000 rows
-- Then uses the ROW_NUMBER function to add a number of days to the #MinDate value, to get a table of incrementing dates
,d(d) as(select top(datediff(day,#MinDate,#MaxDate)+1) dateadd(day,row_number() over (order by (select null))-1,#MinDate) from t,t t2,t t3,t t4,t t5)
select d.d -- Output is a row per date and teamid where there was an open application.
,u.teamid -- When grouped together this gives you a COUNT of open applications by teamid
,count(1) as OpenApplications
from d
join #ApplicationUser as au
on d.d between au.assigneddate and au.unassigneddate
join #users as u
on au.userid = u.id
group by d.d
,u.teamid
order by d.d
,u.teamid
Output:
+------------+--------+------------------+
| d | teamid | OpenApplications |
+------------+--------+------------------+
| 2018-03-01 | 1 | 1 |
| 2018-03-02 | 1 | 1 |
| 2018-03-02 | 2 | 2 |
| 2018-03-03 | 2 | 2 |
| 2018-03-04 | 2 | 1 |
| 2018-03-05 | 2 | 1 |
+------------+--------+------------------+
I have purposefully not pivoted your data to match your desired output as this is almost always a bad idea. As your number of teams change, your number and names of columns outputted will change, which will require constant maintenance. Instead, you should just output a normal set of data and leave the pivoting to the presentation layer once it eventually gets there.
is this use full:
DECLARE #users TABLE (id INT, name VARCHAR(10), teamid INT)
INSERT INTO #users VALUES (1, 'user1', 1),(2, 'user2',2)
DECLARE #application TABLE (id INT, name VARCHAR(15))
INSERT INTO #application VALUES (1, 'application1')
DECLARE #applicationuser TABLE (id INT, applicationid INT, userid INT, assigneddate DATE, unassigneddate DATE)
INSERT INTO #applicationuser VALUES (1,1,1,'2018-03-01','2018-03-02'),(2,1,2,'2018-03-02','2018-03-03')
DECLARE #assignment TABLE([date] DATE,teamid INT,[value] INT)
INSERT INTO #assignment
SELECT [AU].assigneddate [date], [U].teamid, 1 [value]
FROM #applicationuser [AU]
JOIN #users [U] ON [AU].userid = [U].id
INSERT INTO #assignment
SELECT [AU].unassigneddate, [U].teamid,
CASE WHEN LEAD(AssignedDate) OVER(ORDER BY [AU].id)=unassigneddate
THEN -1
ELSE
1
END
FROM #applicationuser [AU]
JOIN #users [U] ON [AU].userid = [U].id
SELECT b.[date],
CASE WHEN t1.teamid=1
THEN 1
ELSE
0
END AS Team1,
CASE WHEN t1.teamid=2
THEN 1
ELSE
0
END AS Team2,
total
FROM
(
SELECT [date], MAX([value]) AS Total
FROM #assignment
group by [date]
)b
INNER JOIN #assignment t1 ON b.Total=t1.Value AND b.[date]=t1.[date]
ORDER BY b.[date]
Thank you very much for all your answers.
I have been trying this myself as well and I seem to have found the answer to my question.
DECLARE #DATE DATETIME
SELECT #DATE = '20180301'
SELECT ApplicationId, Max(UnassignedDate) as UnassignedDate
INTO #TempApplicationUsers
FROM ApplicationUsers
WHERE #DATE > DAteAdd(Day, -1, AssignedDate) and (#DATE < UnassignedDate OR
UnassignedDate is NULL)
Group BY ApplicationId
SELECT * From #TempApplicationUsers
SELECT UserId, COUNT(*) FROM ApplicationUsers, #TempApplicationUsers
WHERE ApplicationUsers.ApplicationId = #TempApplicationUsers.ApplicationId
and ISNULL(ApplicationUsers.UnassignedDate, 0) =
ISNULL(#TempApplicationUsers.UnassignedDate, 0)
GROUP BY UserId
DROP TABLE #TempApplicationUsers
This returns the count for each user on that day and from this I can get the count for each team by the TeamId in the users table.
Thank you again

How to use aggregate function in update in SQL server 2012

I Tried as shown below:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
Expected Output:
Table consists of employeeID's along with amount assigned to Employee I need to subtract amount from amount filed depending on employee usage. Amount "10" should be deducted from ID = 1 and amount "1" should be deducted from ID = 2.
Amount: Credits available for that particular employee depending on date.
So i need to reduce credits from table depending on condition first i need to subtract from old credits. In my condition i need to collect 11 rupees from empID = 1 so first i need to collect 10 rupee from ID=1 and 1 rupee from the next credit i.e ID=2. For this reason in my expected output for ID=1 the value is 0 and final output should be like
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
Need help to update records. Check error in my update statement.
Declare #Deduct int = -11,
#CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set #CurrentDeduct = case when abs(#Deduct) >= AMOUNT then Amount else abs(#Deduct) end
, #Deduct = #Deduct + #CurrentDeduct
,AMOUNT = AMOUNT - #CurrentDeduct
where EmpID= 1
I think you want the following: subtract amounts from 11 while remainder is positive. If this is true, here is a solution with recursive cte:
DECLARE #t TABLE ( id INT, amount INT )
INSERT INTO #t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM #t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM #t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM #t t
JOIN cte c ON c.id = t.id
SELECT * FROM #t
Output:
id amount
1 0
2 0
3 1
4 2
Here I use 17 as start remainder.
If you use sql server 2012+ then you can do it like:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM #t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
First you should get a cumulative sum on amount:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
From here we should put 0 on rows before running_sum exceeds the value 11. Update the row where the running sum exceeds 11 and do nothing to rows after precedent row.
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
From here we can do the update:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
See an SQLDFIDDLE

Multiple counts for each ID

I'm trying to get my head around this, but unfortunately neither of my approaches works:
I need a table with 3 columns:
ItemID
Number cases where ItemID has CostcentreID x
Number cases where ItemID has CostcentreID y
SELECT ItemID, Count1, Count2
FROM Table
Output should be like:
--ItemID--Count1--Count2
1 12 5
2 3 2
What i get when using
SELECT ItemdID, SUM(case when costc...),...
FROM Table
is:
--ItemID--Count1--Count2
1 12 0
2 3 0
due to the GROUP BY statement.
Anyway to solve this without a Cursor?
Also, a JOIN of 5 tables is needed.
Thanks in advance!
I'm not sure what you need with the joins, but here is the first part.
DECLARE #table TABLE(ItemID INT, CostCentreID CHAR(1));
INSERT INTO #table
VALUES (1,'X'),
(1,'X'),
(1,'Y'),
(2,'X'),
(2,'Y'),
(2,'Y'),
(2,'Y');
SELECT ItemID,
SUM(
CASE
WHEN CostCentreID = 'X' THEN 1 ELSE 0
END
) AS CostCentreX,
SUM(
CASE
WHEN CostCentreID = 'Y' THEN 1 ELSE 0
END
) AS CostCentreY
FROM #table
GROUP BY ItemID
Results:
ItemID CostCentreX CostCentreY
----------- ----------- -----------
1 2 1
2 1 3

SQl Server 2005: Rows to columns -- how to do this challenge?

Please let me know, How to convert the following data ,
[id] cost1 cost2 year
1 5 10 2010
1 4 15 2011
2 10 10 2010
into this format [rows of 'Year' to columns heading]
id [cost1-2010] [cost2-2010] [cost1-2011] [cost2-2011]
1 5 10 4 15
2 10 10 0 0
Use PIVOT
example: http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx
try something like this:
DECLARE #YourTable table (id int, cost1 int, cost2 int, year int)
INSERT #YourTable VALUES (1,5,10,2010)
INSERT #YourTable VALUES (1,4,15,2011)
INSERT #YourTable VALUES (2,10,10,2010)
SELECT
id
,SUM(CASE WHEN year=2010 THEN cost1 else 0 END) AS "Cost1-2010"
,SUM(CASE WHEN year=2010 THEN cost2 else 0 END) AS "Cost2-2010"
,SUM(CASE WHEN year=2011 THEN cost1 else 0 END) AS "Cost1-2011"
,SUM(CASE WHEN year=2011 THEN cost2 else 0 END) AS "Cost2-2010"
FROM #YourTable
GROUP BY id
OUTPUT
id Cost1-2010 Cost2-2010 Cost1-2011 Cost2-2010
----------- ----------- ----------- ----------- -----------
1 5 10 4 15
2 10 10 0 0
(2 row(s) affected)
If you want to do this dynamically based on data, it is going to be more difficult than just using PIVOT. For PIVOT or the conditional sum technique
[2010Values] = ( SUM(Case when year = 2010 then FieldValue Else 0 End)
you must know the column name ahead of time.
If you wish to set column names dynamically based on the data received then you'll have to go the dynamic SQL route which can get ugly.
Check out the discussion on this post. That should have you dialed.