Dynamically add a groupid and find the distinct count sql server 2008 - sql-server

Source Data:
TID Pid OpID
1 41 1
1 41 2
1 44 1
1 69 1
1 248 1
1 255 1
1 255 2
4 41 1
4 41 2
4 44 1
4 69 1
4 248 1
4 255 1
4 255 2
6 41 1
6 44 1
6 69 1
6 248 1
6 255 1
6 255 2
15 41 1
15 248 1
15 255 1
15 255 2
16 41 1
16 248 1
16 255 1
16 255 2
output:
Pid OpID Unique TId_count
41 1 2
41 2 2
44 1 2
69 1 2
248 1 2
255 1 2
255 2 2
41 1 1
44 1 1
69 1 1
248 1 1
255 1 1
255 2 1
41 1 2
248 1 2
255 1 2
255 2 2
Rules:
1) At first i need to find what are the available PID and OperationID for each TID
2) later consider PID and OPID as a set(group) and find the possible combination of TID and display distinct TID count
Example:
i) for the PID {41,41,44,69,248,255,255} and OperationId {1,2,1,1,1,1,2} is falling in two tester {1,4} so I need the output TID count is 2.
ii) for the PID {41,44,69,248,255,255} and OperationId {1,1,1,1,1,2} is falling in single tester {6} so i need the output TID count is 1.
iii) Finally, for the PID {41,248,255,255} and OperationId {1,1,1,2} is falling in two tester {15,16} so i need the output TID count is 2.

I cannot imagine any use for this ... but check out this anyway:
select pid, opid, count(*) from
(select tid, pid, opid,
(SELECT ',' + cast(x.pid as nvarchar(10))
FROM #temp x
WHERE x.tid = t.tid
FOR XML PATH ('')) as PidGroup,
(SELECT ',' + cast(x.OpId as nvarchar(10))
FROM #temp x
WHERE x.tid = t.tid
FOR XML PATH ('')) as OpIdGroup
from #temp t) innerselect
group by pid, opid, PidGroup, opidgroup
it produces the output you want ... just replace #temp with your tablename.

Related

Use Sum on extra aggregation and Make Total

I want to create an indexed View.
I have this query:
SELECT srvmn.BBC_ID AS Inventory,
srvInv.ObjectID,
srv.State,
SUM( IIF(srvInv.Direction = srvInv.ReverseBroken, 1, 0)) AS Broken,
SUM(IIF(srvInv.Direction = 0, 1, -1)) AS Sum,
COUNT_BIG(*) AS CountLines
FROM table1 srvmn
JOIN table2 srv
ON srv.ServiceManID = srvmn.ID
JOIN table3 srvInv
ON srv.ID = srvInv.ServiceID
WHERE srv.State > 321
AND srv.State <> 123
GROUP BY srvInv.ObjectID,
srv.State,
srvInv.Direction,
srvInv.ReverseBroken,
srvmn.BBC_ID
ORDER BY srvInv.ObjectID
So, the Sum is not working properly also I fix it with this approach:
SELECT A.Inventory,
A.ObjectID,
A.State,
A.Broken,
SUM(A.Sum) AS Sum
FROM
(
SELECT srvmn.BBC_ID AS Inventory,
srvInv.ObjectID,
srv.State,
IIF(srvInv.Direction = srvInv.ReverseBroken, 1, 0) AS Broken,
SUM(IIF(srvInv.Direction = 0, 1, -1)) AS Sum,
COUNT_BIG(*) AS CountLines
FROM table1 srvmn
JOIN table2 srv
ON srv.ServiceManID = srvmn.ID
JOIN table3 srvInv
ON srv.ID = srvInv.ServiceID
WHERE srv.State > 321
AND srv.State <> 123
GROUP BY srvInv.ObjectID,
srv.State,
srvInv.Direction,
srvInv.ReverseBroken,
srvmn.BBC_ID
) AS A
GROUP BY A.Inventory,
A.ObjectID,
A.State,
A.Broken;
Result is OK, but Derived Tables are not allowed in Indexed Views.
I try to using "Over Partition" in Sum, but the result is not OK.
How I can fix this issue?
Correct Result :
Inventory ObjectID State Broken Sum
------------------------------------------------
NULL 2 500 0 -1
NULL 3 320 1 1
NULL 3 500 0 2
NULL 3 600 0 1
NULL 18 600 0 -1
NULL 20 600 0 -1
1162 20 600 0 1
NULL 24 600 0 1
NULL 26 310 0 -3
NULL 26 320 0 -1
NULL 26 600 0 7
442 26 500 0 8
442 26 500 1 1
442 26 600 0 -5
1162 26 600 0 0
NULL 27 500 0 4
NULL 27 600 0 2
1162 27 600 0 1
1162 28 600 0 3
Not Ok Result:
Inventory ObjectID State Broken Sum CountLines
-------------------------------------------------------------
NULL 2 500 0 -1 1
NULL 3 500 0 2 2
NULL 3 600 0 1 1
NULL 18 600 0 -1 1
1162 20 600 0 3 3
1162 20 600 0 -2 2
NULL 20 600 0 1 1
NULL 20 600 0 -2 2
NULL 24 600 0 -1 1
NULL 24 600 0 2 2
442 26 600 0 -7 7
NULL 26 600 0 -18 18
1162 26 600 0 1 1
NULL 26 600 0 25 25
442 26 500 1 1 1
442 26 600 0 2 2
1162 26 600 0 -1 1
442 26 500 0 8 8
NULL 27 600 0 -3 3
1162 27 600 0 1 1
Please try it :
SELECT
srvmn.BBC_ID AS Inventory,
srvInv.ObjectID,
srv.State,
SUM(IIF(srvInv.Direction = srvInv.ReverseBroken, 1, 0)) AS Broken,
SUM(IIF(srvInv.Direction = 0, 1, -1)) AS Sum
FROM table1 srvmn
JOIN table2 srv ON srv.ServiceManID = srvmn.ID
JOIN table3 srvInv ON srv.ID = srvInv.ServiceID
WHERE srv.State > 321 AND srv.State <> 123
GROUP BY
srvInv.ObjectID,
srv.State,
srvInv.ReverseBroken,
srvmn.BBC_ID
You should not use Field "srvInv.Direction" in "Group By"

SQL Pivot Table - Subqueries

I have a program that is logging the time a user spends on certain aspects, some are identified as specific "time". I'm struggling to get multiple lines of Grouped query results into a single line for each month as a "summary".
My current query:
SELECT
TotalMins = SUM(Minutes)
,DateMonth = MONTH(Date)
,ID1
,PC
FROM User_Time_Log
WHERE
(UserID = 1)
AND (YEAR(Date) = 2018)
GROUP BY
MONTH(Date)
,ID1
,PC1
Current results:
TotalMins DateMonth ID1 PC1
192 1 0 0
306 1 0 100
113 2 0 0
365 2 0 100
14 2 1 0
3 2 1 100
75 3 0 0
253 3 0 100
3 3 1 0
300 4 0 0
233 4 0 100
10 4 1 0
23 4 1 100
438 5 0 0
134 5 0 100
19 5 1 0
49 5 1 100
0 9 1 0
11 10 0 0
21 10 0 60
167 10 1 100
What I would like to do from this point is to create a table showing all 12 months, regardless of whether there is information within that month or not, and show the relative information within each row for that month. for example:
DateMonth NonID1 TimeID1 TimePC1 (Round((PC1/100)*TotalMins)) TimePC1ID1
1 192 0 306 0
2 113 14 365 3
3 75 3 253 0
4 300 10 233 23
5 438 19 134 49
6 0 0 0 0
7 0 0 0 0
8 0 0 0 0
9 0 0 0 0
10 11 0 13 167
11 0 0 0 0
12 0 0 0 0
What's the most efficient way to do this?
Note: I have also created a table to give me 1-12 as rows that I can use to give me the months that I need to use, where information is not within the user_time_log.
Here's a simple way to do what you're looking for:
First, create your table of month values. I made a simple temp table with a single column.
CREATE TABLE #Dates (MonthNum INT)
INSERT INTO #Dates
(
MonthNum
)
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
Next, you can put your existing query into a CTE, then LEFT JOIN to your table of months. You'll want to put your columns into a SUM'd CASE statement, like so:
;WITH Aggregation AS
(
SELECT
TotalMins = SUM(Minutes)
,DateMonth = MONTH(Date)
,ID1
,PC1
FROM #User_Time_Log
WHERE
(UserID = 1)
AND (YEAR(Date) = 2018)
GROUP BY
MONTH(Date)
,ID1
,PC1
)
SELECT
d.MonthNum
,NonID1 = SUM(CASE WHEN ID1 = 0 THEN TotalMins ELSE 0 END)
,TimeID1 = SUM(CASE WHEN ID1 = 1 THEN TotalMins ELSE 0 END)
,TimePC1 = SUM(CASE WHEN ID1 = 0 THEN ROUND((PC1/100)*TotalMins,0) ELSE 0 END)
,TimePC1ID1 = SUM(CASE WHEN ID1 = 1 THEN ROUND((PC1/100)*TotalMins,0) ELSE 0 END)
FROM #Dates d
LEFT JOIN Aggregation a ON d.MonthNum = a.DateMonth
GROUP BY d.MonthNum
Output would then look like this:
MonthNum NonID1 TimeID1 TimePC1 TimePC1ID1
1 498 0 306 0
2 478 17 365 3
3 328 3 253 0
4 533 33 233 23
5 572 68 134 49
6 0 0 0 0
7 0 0 0 0
8 0 0 0 0
9 0 0 0 0
10 32 167 0 167
11 0 0 0 0
12 0 0 0 0
EDIT:
The ROUND() function call can be changed slightly to accomodate your need for decimal results. The first parameter of ROUND() is the expression you want to round, and the second is the number of decimal places to round to. Positive numbers indicate the number of places to the right of the decimal to round to. Negative numbers indicate the number of places to the left of the decimal to round to. So if you set it to 2, you'll get an answer rounded to the nearest hundredth.
But there's one more tweak we need. PC1 and TotalMins are both assumed to be INTs in my answer. So we have to give the SQL engine a little help so that it calculates the answer as a DECIMAL. By CAST()ing the INTs to DECIMALs, SQL will perform the arithmetic op as decimal math instead of integer math. You'd just have to change TimePC1 and TimePC1ID1 like so:
,TimePC1 = SUM(CASE WHEN ID1 = 0 THEN ROUND((CAST(PC1 AS DECIMAL)/100)*CAST(TotalMins AS DECIMAL),2) ELSE 0 END)
,TimePC1ID1 = SUM(CASE WHEN ID1 = 1 THEN ROUND((CAST(PC1 AS DECIMAL)/100)*CAST(TotalMins AS DECIMAL),2) ELSE 0 END)
Then the output looks like this:
MonthNum NonID1 TimeID1 TimePC1 TimePC1ID1
1 498 0 306.000000 0.000000
2 478 17 365.000000 3.000000
3 328 3 253.000000 0.000000
4 533 33 233.000000 23.000000
5 572 68 134.000000 49.000000
6 0 0 0.000000 0.000000
7 0 0 0.000000 0.000000
8 0 0 0.000000 0.000000
9 0 0 0.000000 0.000000
10 32 167 12.600000 167.000000
11 0 0 0.000000 0.000000
12 0 0 0.000000 0.000000

select timestamps within ranges

I have got 2 tables. Example Table 1:
ID episode_id episode_start episode_end
----------------------------------------------------
1 1 1 2
1 2 4 5
1 3 96 105
1 4 110 114
2 1 1 4
2 2 13 24
Example Table 2
ID timestamp Other_info
--------------------------------
1 1 111
1 2 142
1 3 114
1 4 112
1 5 116
1 6 123
2 1 145
2 2 156
2 3 154
I would like to merge the two tables based upon table2.timestamp = between table1.episode_start and table1.episode_end.
The final table should be a subset of table 2 with only the timestamprows where there is an episode.
Question: How to do this? What is the most computational efficient way?
EDIT: In reallity my tables are much longer.
So, for example, the episode_start of ID = 1 be the same as the episode start of ID=1200 in episode 12. So simply merging them does not work.
EDIT: EXPECTED OUTPUT:
ID timestamp Other_info
--------------------------------
1 1 111
1 2 142
1 4 112
1 5 116
2 1 145
2 2 156
2 3 154
and so on. The merge is basically some sort of filter of table 2 by table 1.
select table2.id, timestamp, other_info
from table1
left join table2 on table2.timestamp between table1.episode_start and table1.episode_end
where table2.id = table1.id
Output:
id timestamp Other_info
---------------------------------------
1 1 111
1 2 142
1 4 112
1 5 116
2 1 145
2 2 156
2 3 154

SQL query to get Groups and sub groups hierarchy

Account table
ac_id ac_name st_id
----------- ------------- -----------
1 LIABILITES 1
2 ASSET 1
3 REVENUE 1
4 EXPENSES 1
5 EQUITY 1
Groups table
grp_id grp_name ac_no grp_of st_id type_ cmp_id
----------- ------------------- ---------- -------- --------- --------- --------
1 Capital Account 1 0 1 0 0
2 Current Liability 1 0 1 0 0
3 Loan Liability 1 0 1 0 0
4 Suspense A/C 1 0 1 0 0
5 Current Assets 2 0 1 0 0
6 Fixed Assests 2 0 1 0 0
7 Investment 2 0 1 0 0
8 Misc. Expenses 2 0 1 0 0
9 Direct Income 3 0 1 0 0
10 Indirect Income 3 0 1 0 0
11 Sale Account 3 0 1 0 0
12 Direct Expense 4 0 1 0 0
13 Indirect Expense 4 0 1 0 0
14 Purchase Account 4 0 1 0 0
15 Sundry Creditors 2 1 1 0 0
16 Sundry Debitors 5 1 1 0 0
17 Bank Account 5 1 1 0 0
18 Cash In Hand 5 1 1 0 0
19 Duties & Taxes 2 1 1 0 0
20 Salary 12 1 1 0 0
21 Personal 5 1 1 0 0
22 Loan 2 0 1 0 0
23 Customer 16 1 1 0 0
34 Vendor 15 1 1 0 0
38 Sale Softwares 11 1 1 1 1
46 Stock In Hand 5 1 1 1 1
47 test 1 1 1 1 1
48 test in 47 1 1 1 1
Query to get all groups hierarchy.
declare #ac_no as int =2
;With CTE(grp_id,grp_name,ac_no,Level)
AS
( SELECT
grp_id,grp_name,ac_no,CAST(1 AS int)
FROM
Groups
WHERE
grp_id in (select grp_id from Groups where (ac_no=#ac_no) and grp_of=0)
UNION ALL
SELECT
o.grp_id,o.grp_name,o.ac_no,c.Level+1
FROM
Groups o
INNER JOIN
CTE c
ON c.grp_id=o.ac_no --where o.ac_no=2 and o.grp_of=1
)
select * from CTE
Result is ok for ac_no=2/3/4
grp_id grp_name ac_no Level
----------- ------------------- ----------- ------
5 Current Assets 2 1
6 Fixed Assests 2 1
7 Investment 2 1
8 Misc. Expenses 2 1
22 Loan 2 1
16 Sundry Debitors 5 2
17 Bank Account 5 2
18 Cash In Hand 5 2
21 Personal 5 2
46 Stock In Hand 5 2
23 Customer 16 3
But when I try to get result for ac_no=1;
I get error :
Msg 530, Level 16, State 1, Line 4
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
I think the issue is that you end up in an infinite recursion as you have a row that is it's own parent/child (eg. grp_id = ac_no).
I think it should work if you add a limiting clause to the recursive member like this:
DECLARE #ac_no AS int = 1;
WITH CTE (grp_id , grp_name , ac_no , Level ) AS (
SELECT grp_id, grp_name, ac_no, CAST( 1 AS int )
FROM Groups
WHERE grp_id IN (SELECT grp_id FROM Groups WHERE ac_no = #ac_no AND grp_of = 0)
UNION ALL
SELECT o.grp_id, o.grp_name, o.ac_no, c.Level + 1
FROM Groups o
INNER JOIN CTE c ON c.grp_id = o.ac_no --where o.ac_no=2 and o.grp_of=1
WHERE c.ac_no <> c.grp_id
)
SELECT * FROM CTE;

Order By using case with conditional results and partition row_number()

I have a SQL that I've not been able to return the order correctly. Below is example of the rows and the order I want them to appear in. I tried ORDER BY WITH CONDITIONAL CASE AND ROW_NUMBER OVER PARTION without success. I want ColA to be the primary sort and ColB secondary only when ColC has a length < 3 otherwise ColB is primary and ColA is secondary
ColA ColB ColC
5 750 15
5 750 15
3 984 13
3 984 13
5 1021 15
5 1021 15
4 1602 14
4 1602 14
4 1823 14
4 1823 14
6 4099 16
6 4099 16
11 4099 240990
0 10880 10
0 10880 10
3 10881 13
3 10881 13
2 11053 12
8 11053 211053
6 10891 16
6 10891 16
2 11034 12
10 11034 211034
ColA ColB ColC
0 10880 10
0 10880 10
2 11034 12
10 11034 211034
2 11053 12
8 11053 211053
3 984 13
3 984 13
3 10881 13
3 10881 13
4 1602 14
4 1602 14
4 1823 14
4 1823 14
5 750 15
5 750 15
5 1021 15
5 1021 15
6 4099 16
6 4099 16
11 4099 240990
6 10891 16
6 10891 16
order by case when len(ColC) < 3 then ColA else ColB end,
case when len(ColC) < 3 then ColB else ColA end
SQL Fiddle Example

Resources