I need 2 count values from single column to be in same row using Over () clause - sql

I have the following Result from Select statement
UnitId UnitType GroupId
1 1 1
2 1 1
3 1 2
4 2 2
5 2 2
6 2 2
7 2 2
I need the following result for each group Id
GroupId CountBasedOnUnitType1 CountBasedOnUnitType2
1 2 0
2 1 4
Thanks in advance.

Try this
SELECT * FROM
(
SELECT GroupId,
UnitType
FROM Table1
) x
Pivot
(
Count(UnitType)
For UnitType in ([1], [2])
) p
Fiddle Demo
Output
GroupId 1 2
1 2 0
2 1 4

Does in necessarily need to have OVER?
select
GroupID,
sum(case when UnitType = 1 then 1 else 0 end) CountBasedOnUnitType1,
sum(case when UnitType = 2 then 1 else 0 end) CountBasedOnUnitType2
from table
group by GroupID

Related

Select top rows until value in specific column has appeared twice

I have the following query where I am trying to select all records, ordered by date, until the second time EmailApproved = 1 is found. The second record where EmailApproved = 1 should not be selected.
declare #Test table (id int, EmailApproved bit, Created datetime);
insert into #Test (id, EmailApproved, Created)
values
(1,0,'2011-03-07 03:58:58.423')
, (2,0,'2011-02-21 04:55:52.103')
, (3,0,'2011-01-29 13:24:02.103')
, (4,1,'2010-10-12 14:41:54.217')
, (5,0,'2010-10-12 14:34:15.903')
, (6,0,'2010-10-12 10:10:19.123')
, (7,1,'2010-08-27 12:07:16.073')
, (8,1,'2010-08-25 12:15:49.413')
, (9,0,'2010-08-25 12:14:51.970')
, (10,1,'2010-04-12 16:43:44.777');
select *
, case when Row1 = Row2 then 1 else 0 end Row1EqualRow2
from (
select id, EmailApproved, Created
, row_number() over (partition by EmailApproved order by Created desc) Row1
, row_number() over (order by Created desc) Row2
from #Test
) X
--where Row1 = Row2
order by Created desc;
Which produces the following results:
id EmailApproved Created Row1 Row2 Row1EqualsRow2
1 0 2011-03-07 03:58:58.423 1 1 1
2 0 2011-02-21 04:55:52.103 2 2 1
3 0 2011-01-29 13:24:02.103 3 3 1
4 1 2010-10-12 14:41:54.217 1 4 0
5 0 2010-10-12 14:34:15.903 4 5 0
6 0 2010-10-12 10:10:19.123 5 6 0
7 1 2010-08-27 12:07:16.073 2 7 0
8 1 2010-08-25 12:15:49.413 3 8 0
9 0 2010-08-25 12:14:51.970 6 9 0
10 1 2010-04-12 16:43:44.777 4 10 0
What I actually want is:
id EmailApproved Created Row1 Row2 Row1EqualsRow2
1 0 2011-03-07 03:58:58.423 1 1 1
2 0 2011-02-21 04:55:52.103 2 2 1
3 0 2011-01-29 13:24:02.103 3 3 1
4 1 2010-10-12 14:41:54.217 1 4 0
5 0 2010-10-12 14:34:15.903 4 5 0
6 0 2010-10-12 10:10:19.123 5 6 0
Note: Row, Row2 & Row1EqualsRow2 are just working columns to show my calculations.
Steps:
Create a row number, rn, over all rows in case id is not in sequence.
Create a row number, approv_rn, partitioned by EmailApproved so we know when EmailApproved = 1 for the second time
Use a outer apply to find the row number of the second instance of EmailApproved = 1
In the where clause filter out all rows where the row number is >= the value found in step 3.
If there is 1 or 0 EmailApproved records available then the outer apply will return null, in which case return all available rows.
with test as
(
select *,
rn = row_number() over (order by Created desc),
approv_rn = row_number() over (partition by EmailApproved
order by Created desc)
from #Test
)
select *
from test t
outer apply
(
select x.rn
from test x
where x.EmailApproved = 1
and x.approv_rn = 2
) x
where t.rn < x.rn or x.rn is null
order by t.Created desc;

Select first date in which an event happen for each id

I have a series of Ids, some of them activate a product on certain month and that product remains activated for an X period of time, while others do not activate the product.
I want to create a column which indicates in which month the user activates the product or a NULL if the user doesn't activate it.
I've tried using a partition like the following:
SELECT id, fl_testdrive, month_dt,
CASE WHEN fl_testdrive = 1 then min(month_dt) OVER(PARTITION BY id ORDER BY month_dt ROWS UNBOUNDED PRECEDING) else 0 end as month_testdrive
FROM Table_1
However, when I try this solution, in the column month_testdrive, I do not obtain the first month in which the user appears, indepently of if he/she activated that product in that month or on a later one.
This is what I get with my query
Id flag_testdrive month_dt month_testdrive
1 0 1 1
1 0 2 1
1 1 3 1
1 1 4 1
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
What I'd expect:
Id flag_testdrive month_dt month_testdrive
1 0 1 3
1 0 2 3
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
This solution is a second best but is also fine:
Id flag_testdrive month_dt month_testdrive
1 0 1 0
1 0 2 0
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
You want CASE expression inside MIN():
MIN(CASE WHEN fl_testdrive = 1 THEN month_dt ELSE 0 END) OVER(PARTITION BY id, flag_testdrive ORDER BY month_dt ROWS UNBOUNDED PRECEDING)
Here's an option for you:
DECLARE #Testdate TABLE(
id INT
,flag_testdrive INT
,month_dt INT
)
INSERT INTO #Testdate (
[id]
, [flag_testdrive]
, [month_dt]
)
VALUES(1,0,1)
,(1,0,2)
,(1,1,3)
,(1,1,4)
,(2,0,2)
,(2,0,3)
,(3,1,4)
,(3,1,5)
SELECT
*
,COALESCE((SELECT MIN([aa].[month_dt]) FROM #Testdate aa
WHERE aa.[id] = a.id
AND aa.[flag_testdrive] = 1), 0) AS month_testdrive
FROM #Testdate a
Return the minimum month_dt for a given id only if flag_testdrive=1, wrapped in coalesce to return 0 instead of NULL.

Update table records with accumulated result

Lets say I have a table Tbl (Represents simple timelogs for work made on different customers)
Five columns
Id: int
TimeUse: float
IdCustomer: int
Created: DateTime
TimeCalc: float
I have a number of records in this table, (TimeCalc is initialized to value = 0)
What I want my SQL to do is:
when TimeUse for all foregoing records on a specific customer accumulates to a value < 10 then the value in TimeCalc should be 0
when TimeUse for all foregoing records on a specific customer accumulates to a value >= 10 then the value in TimeCalc should be = TimeUse for the record...
I have messed around with Case routines with subqueries, but can't get it working.
BEFORE
Id TimeUse IdCustomer Created TimeCalc
1 2 1 14/09/09 0
2 5 2 14/09/10 0
3 2 1 14/09/11 0
4 5 2 14/09/12 0
5 4 1 14/09/13 0
6 2 2 14/09/14 0
7 4 1 14/09/15 0
8 1 1 14/09/16 0
9 3 2 14/09/17 0
10 2 1 14/09/18 0
11 4 2 14/09/19 0
AFTER
Id TimeUse IdCustomer Created TimeCalc
1 2 1 14/09/09 0
2 5 2 14/09/10 0
3 2 1 14/09/11 0
4 5 2 14/09/12 0
5 4 1 14/09/13 0
6 2 2 14/09/14 2
7 4 1 14/09/15 0
8 1 1 14/09/16 1
9 3 2 14/09/17 3
10 2 1 14/09/18 2
11 4 2 14/09/19 4
Can this be solved in an SQL update?
In SQL Server 2012+, you can do this with a cumulative sum:
select Id, TimeUse, IdCustomer, Created,
(case when sum(timeuse) over (partition by idcustomer order by id) < 10 then 0
else timeuse
end) as timecalc
from table t;
You can do the same thing in earlier versions using outer apply or a subquery.
If you want an update, just use a CTE:
with toupdate as (
select t.*,
(case when sum(timeuse) over (partition by idcustomer order by id) < 10 then 0
else timeuse
end) as new_timecalc
from table t
)
update toupdate
set timecalc = new_timecalc;
EDIT:
The following will work in any version of SQL Server:
with toupdate as (
select t.*,
(case when (select sum(t2.timeuse)
from table t2
where t2.idcustomer = t.idcustomer and
t2.id <= t.id
) < 10 then 0
else timeuse
end) as new_timecalc
from table t
)
update toupdate
set timecalc = new_timecalc;

How can we count sub query use group by without pivot?

I need to create query that has below result.
I add my table for example for better understanding.
QTable:
QID Name
-------
1 x
1 y
2 z
FTable:
QID FID
-------
1 1
1 2
2 3
Inner join with :
FID Type
-------
1 1
1 2
1 3
1 4
2 2
2 3
2 3
3 1
3 3
3 3
I need to result like this:
result:
QID FID-Count Type(1)-count Type(2)-count Type(3)-count Type(4)-count
-----------------------------------------------------------------
1 2 1 2 3 1
2 1 1 0 2 0
Thank u for help.
Ususally you can emulate a pivot with GROUP BY and SUM+CASE:
SELECT
T1.QID,
COUNT(DISTINCT T1.FID) as FIDCOUNT,
SUM(CASE WHEN Type=1 THEN 1 ELSE 0 END) as Type1Cnt,
SUM(CASE WHEN Type=2 THEN 1 ELSE 0 END) as Type2Cnt,
SUM(CASE WHEN Type=3 THEN 1 ELSE 0 END) as Type3Cnt,
SUM(CASE WHEN Type=4 THEN 1 ELSE 0 END) as Type4Cnt
FROM QFTable as T1
JOIN T2 ON
T1.FID=T2.FID
GROUP BY T1.QID
ORDER BY QID
SQLFiddle demo

Group by in Subquery

I have a table AssignmentMaster in that I have following columns with data
AssignmentID PaidAmount RefundAmount UserID
1 20 0 1
2 10 5 1
3 30 7 2
4 25 0 3
5 35 15 3
6 10 3 1
7 5 0 3
8 10 0 3
Now I want to find out the TotalNumberofAssignment with respect to userID, i.e. result should be:
UserID TotalAssignment TotalAssignmentofRefundAmount TotalPaidAmount TotalRefundAmount
1 3 2 40 8
2 1 1 30 7
3 4 1 75 15
How I can get above given result in MSSQL.
your any help will help me lot.
SELECT
UserID,
COUNT(AssignmentID) AS TotalAssignment,
SUM(SIGN(RefundAmount)) AS TotalAssignmentofRefundAmount,
SUM(PaidAmount) AS TotalPaidAmount,
SUM(RefundAmount) AS TotalRefundAmount
FROM
MyTable
GROUP BY
UserID
Note:
SIGN(RefundAmount) works if RefundAmount is always >= 0.
If not, change to
SUM(CASE WHEN RefundAmount <> 0 THEN 1 ELSE 0 END) AS TotalAssignmentofRefundAmount
Select UserID,
count(1) as TotalAssignment,
sum( case when RefundAmount = 0 then 0 else 1 end) as TotalAssignmentofRefundAmount,
sum(PaidAmount) as TotalPaidAmount ,
sum(RefundAmount) as TotalRefundAmount
From AssignmentMaster
Group by UserID
To show how to do this using nested SQL:
Select UserTotals.UserID, UserTotals.TotalAssignment,
Refunds.TotalAssignmentofRefundAmount,
UserTotals.TotalPaidAmount, UserTotals.TotalRefundAmount
From (select UserID,
Count(AssignmentID) [TotalAssignment],
Sum(PaidAmount) [TotalPaidAmount],
sum(RefundAmount) [TotalRefundAmount]
From #AssignmentMaster
Group By UserID
) [UserTotals] Left Join
(Select UserID,
Count(AssignmentID) [TotalAssignmentofRefundAmount]
From #AssignmentMaster
Where RefundAmount > 0
Group By UserID
) [Refunds] On Refunds.UserID = UserTotals.UserID
select UserId, count (AssignmentID) as TotalAssignment,
sum(case when RefundAmount = 0 then 0 else 1 end) as TotalAssignmentofRefundAmount,
sum(PaidAmount) as TotalPaidAmound,
sum(RefundAmount) as TotalRefundAmount
from AssignmentMaster
group by UserID;