How to use Pivot table for calculating in this scenario? - c#

How to get the below result from the given data.
Report
-------------------------------------------------
ID | Name | Status
-------------------------------------------------
1 | A | Inprogress
2 | A | Inprogress
3 | A | Complete
I need to calculate the result like
---------------------------------------------
Name | Total | Complete | Remaining
---------------------------------------------
A | 3 | 1 | 2
SQL:
Select
[Inprogress], [Complete], [Printed]
from
(select
specimenID, count([RStatus])
from tbl_LabReport
group by specimenID) as sourcetable
Pivot
(Count(specimenID) for [RStatus] in ( [Inprogress],[Complete],[Printed] )) as ptv

You don't need to use PIVOT operator. Try to use subquery or CROSS APPLY operator to solve your task
;WITH test_data AS(
SELECT 1 AS id, 'A' AS NAME, 'Inprogress' AS [status]
UNION ALL
SELECT 2, 'A', 'Inprogress'
UNION ALL
SELECT 3, 'A', 'Complete'
UNION ALL
SELECT 4, 'B', 'Complete'
)
SELECT DISTINCT
name,
T3.*
FROM test_data AS T
CROSS APPLY (SELECT COUNT(*) AS total,
SUM(CASE WHEN T2.status = 'Complete' THEN 1 ELSE 0 END) AS complete,
SUM(CASE WHEN T2.status = 'Inprogress' THEN 1 ELSE 0 END) AS remaining
FROM test_data AS T2
WHERE T2.name = T.name) AS T3

There are a few approaches you could take. This example uses CASE expressions. If you are not familiar with the concept a CASE allows you to conditional return a selected value. I've converted your sample data into table variable:
Sample Data
/* Creating sample data inside a table variable makes it
* easy to share.
*/
DECLARE #Sample TABLE
(
ID INT,
Name VARCHAR(50),
[Status] VARCHAR(50)
)
;
/* Sample values taken from OP.
*/
INSERT INTO #Sample
(
ID,
Name,
[Status]
)
VALUES
(1, 'A', 'Inprogress'),
(2, 'A', 'Inprogress'),
(3, 'A', 'Complete')
;
The CASE returns a 1 for the required Status and a 0 for everything else. Summing the result provides sub totals.
/* Using CASE to return conditional sub totals.
*/
SELECT
Name,
COUNT([Status]) AS Total,
SUM(CASE WHEN [Status] = 'Complete' THEN 1 ELSE 0 END) AS Complete,
SUM(CASE WHEN [Status] = 'Inprogress' THEN 1 ELSE 0 END) AS Remaining
FROM
#Sample
GROUP BY
Name
;

Related

Update each row value until it goes 0

I'm an creating an inventory program for a company .I have a table with data on SQL Server
|product|stock |
|A |15 |
|B |26 |
|C |27 |
|D |18 |
I want to subtract a quantity x=70 on each product on the table until the product goes 0. For example: subtract x on the product A until it goes 0 (70-15=55). Then this difference subtracts the Product B until it goes 0 (55-26=29). And so on the Product C (29-27=2). Then on Product D (18-2=16).
In the end the table would be:
|product|stock |
|A |0 |
|B |0 |
|C |0 |
|D |16 |
I need a way to do this is SQL database.Thanks
You could use SUM()...OVER()
DECLARE #SampleTable AS TABLE
(
Product varchar(10),
Stock int
)
INSERT INTO #SampleTable
VALUES ('A', 15),('B', 26),('C', 27),('D', 18), ('E', 20)
DECLARE #TotalStock int = 70
SELECT st.Product ,
CASE
WHEN sum(st.Stock) OVER(ORDER BY (st.Product)) < #TotalStock THEN 0
WHEN sum(st.Stock) OVER(ORDER BY (st.Product)) - #TotalStock < st.Stock THEN sum(st.Stock) OVER(ORDER BY (st.Product)) - #TotalStock
ELSE st.Stock
END AS stock
FROM #SampleTable st
Another approach you could use CROSS APPLY
;WITH temp AS
(
SELECT *, Row_number() OVER(ORDER BY(SELECT 1)) AS RowIndex
FROM #SampleTable st
)
SELECT st.Product,
CASE
WHEN ca.realstock < 0 THEN 0
WHEN ca.realstock < st.Stock THEN ca.realstock
ELSE st.Stock
END AS stock
FROM temp st
CROSS APPLY
(
SELECT isnull(sum(st2.Stock),0) - #TotalStock AS realstock
FROM temp st2
WHERE st2.RowIndex <= st.RowIndex
) ca
See demo for 2 approaches: Rextester
You can use window function sum to find cumulative sum of stock in the order of product (check if you need ordering on some other basis) and use that a filter rows:
select *
from (
select t.*,
sum(stock) over (
order by product
) as cumulative_sum
from your_table t
) t
where cumulative_sum - stock <= 70;

CTE with Pivot Table

I am having a table : TblMemberPosition
Running a sql code -
with cte(MemberID, SponserID, Level)
as
(
select MemberID, SponserID, 1 as Level
from TblMemberPosition
where MemberID = 10021
union all
select a.MemberID, a.SponserID , Level + 1
from TblMemberPosition as a
inner join cte as b
on b.MemberID = a.SponserID
)
select (count (MemberID)) as MemberCount, Level
From cte
where Level <= 8
Group by Level
order by Level
Result of above query:
MemberCount Level
1 1
1 2
3 3
1 4
2 5
1 6
1 7
2 8
But I want output Like -
MemberID Level1 Level 2 Level 3 Level 4 .... Level upto 9
10021 1 1 3 1
This code is generating for single MemberId. Can you help to generate this structure for all members.
with cte(MemberID, SponserID, Level)
as
(
select MemberID, SponserID,1 as Level from TblMemberPosition
where MemberID = 10021
union all
select a.MemberID,a.SponserID,Level + 1 from TblMemberPosition
as a inner join cte as b on b.MemberID = a.SponserID
),
ctePivotSource as (
select
(count (MemberID)) as MemberCount,
'Level ' + convert(varchar(1),[Level]) as [Level]
from cte
where Level <=8
group by level
)
select *
from
( select [MemberID],[MemberCount],[Level] from ctePivotSource) src
pivot
(
sum(src.MemberCount) FOR [Level] in ([Level 1],[Level 2],[Level 3],[Level 4],[Level 5],[Level 6],[Level 7],[Level 8])
) as pvt
You original query restricts the columns to 8 (not 9 as in the required result) so I have also limited the pivot to 8 columns.
This should give you want you want for root level members, meaning members who do not have a sponsor.
;WITH cte AS
(
SELECT MemberID,
SponserID,
MemberID AS [Root] -- this will be your grouping
0 AS [Level] -- not sure you want to count yourself
FROM TblMemberPosition
WHERE SponsorID IS NULL -- get highest level members only?
UNION ALL
SELECT a.MemberID,
a.SponserID,
[Root],
[Level] + 1
FROM TblMemberPosition AS a
INNER JOIN cte AS b ON b.MemberID = a.SponserID
)
SELECT [Root] AS MemberID,
COUNT(CASE WHEN [Level] = 1 THEN 1 END) AS [Level1],
COUNT(CASE WHEN [Level] = 2 THEN 1 END) AS [Level2],
COUNT(CASE WHEN [Level] = 3 THEN 1 END) AS [Level3],
COUNT(CASE WHEN [Level] = 4 THEN 1 END) AS [Level4],
COUNT(CASE WHEN [Level] = 5 THEN 1 END) AS [Level5],
COUNT(CASE WHEN [Level] = 6 THEN 1 END) AS [Level6],
COUNT(CASE WHEN [Level] = 7 THEN 1 END) AS [Level7],
COUNT(CASE WHEN [Level] = 8 THEN 1 END) AS [Level8],
COUNT(CASE WHEN [Level] = 9 THEN 1 END) AS [Level9]
FROM cte
GROUP BY [Root]

How to get time ranges from a table with time-based digital values?

I do have a (SQL Server) database table that contains an activity log of a device. The table consists of a DeviceId, a timestamp (DateTime) and a value field. The device writes its state changes to the DB whenever it turns on or off (value 1 resp. 0).
Now I wonder what would be the fastest way to get "blocks of activity" from that table. What do I mean by that? I'd like to get all time periods that are defined by a "1" value and its subsequent "0" value for a given DeviceId, so that I get a list of time ranges like this (for the active blocks, the inactive times would be between a 0 value followed by a 1):
DateTime ActiveStart, DateTime ActiveEnd
I currently ended up by first getting all the entries with EF as a list, then looping over them and comparing each entry to its predecessor in order to check if the device had been turned on and off.
That does work, but I do think that there must be a better and more performant way of doing this. What would be the best way to do it? Either a pure SQL query (from which I could build me a Stored Procedure) or a LINQ to SQL query will do.
Thanks for your thoughts and comments!
--------------------------
------ sample data -------
--------------------------
declare #t table
(
DeviceId int,
Timestamp DateTime,
Value bit
)
insert into #t values
(1, '2016-01-01', 1),
(1, '2016-01-05', 1),
(1, '2016-01-07', 1),
(1, '2016-01-08', 0),
(1, '2016-01-10', 0),
(1, '2016-01-21', 0),
(1, '2016-01-22', 1),
(1, '2016-01-25', 0),
(2, '2016-01-02', 1),
(2, '2016-01-04', 0),
(2, '2016-01-06', 1),
(2, '2016-01-08', 0),
(2, '2016-01-09', 1),
(2, '2016-01-15', 0),
(2, '2016-01-18', 1)
--------------------------
---------- query ---------
--------------------------
select
DeviceId,
gr,
ActiveStart = max(case when Value = 1 then Timestamp end),
ActiveEnd = max(case when Value = 0 then Timestamp end)
from
(
select
*,
gr = Value + row_number() over(partition by DeviceId order by Timestamp)
from #t
) t
group by DeviceId, gr
-- optional sorting by dates for easier results evaluation:
--order by DeviceId,
-- case when max(case when value = 1 then Timestamp end) is NULL
-- then max(case when value = 0 then Timestamp end)
-- else max(case when value = 1 then Timestamp end) end
You might try it like this:
CREATE TABLE #deviceLog (DeviceID INT, Activity DATETIME,Stat INT);
INSERT INTO #deviceLog VALUES
(1,{ts'2016-04-04 11:20:00'},1)
,(1,{ts'2016-04-04 11:30:00'},0)
,(1,{ts'2016-04-04 11:33:00'},1)
,(1,{ts'2016-04-04 11:38:00'},0)
,(2,{ts'2016-04-04 12:33:00'},1)
,(2,{ts'2016-04-04 12:40:00'},0)
,(3,{ts'2016-04-04 10:33:00'},1)
,(3,{ts'2016-04-04 11:38:00'},0);
WITH AllOn AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY DeviceID ORDER BY Activity) AS Inx,*
FROM #deviceLog
WHERE Stat=1
)
,AllOff AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY DeviceID ORDER BY Activity) AS Inx,*
FROM #deviceLog
WHERE Stat=0
)
SELECT AllOn.*,AllOff.Activity AS OffActivity
FROM AllOn
INNER JOIN AllOff ON AllOn.DeviceID=AllOff.DeviceID AND AllOn.Inx=AllOff.Inx;
DROP TABLE #deviceLog;
The result
Inx DeviceID Activity Stat OffActivity
1 1 2016-04-04 11:20:00.000 1 2016-04-04 11:30:00.000
2 1 2016-04-04 11:33:00.000 1 2016-04-04 11:38:00.000
1 2 2016-04-04 12:33:00.000 1 2016-04-04 12:40:00.000
1 3 2016-04-04 10:33:00.000 1 2016-04-04 11:38:00.000
SQL Server 2012+ supports cumulative sums. You can get blocks of activity by counting the number of *0*s cumulatively. A block of activity will have a constant value. Then you can aggregate (and filter) to get the periods of activity:
select deviceid, min(timestamp), max(timestamp)
from (select t.*,
sum(case when value = 0 then 1 else 0 end) over
(partition by deviceid order by timestamp) as grp
from t
) t
where value <> 0
group by deviceid, grp;
In earlier versions of SQL Server, you can do something similar using outer apply (and by other methods).

How to Show logical Ids on table

I have table of employees and their floor as shown:
ID EmployeeId EmpName FloorId
1 abc123 abc 1
2 xyz123 xyz 1
3 def123 def 2
4 pqr123 pqr 2
I have shown result from table on floor wise basis,but when I shows 2nd floor employees I want result to be shown like this:
select * from tblname where FloorId=2
ID EmployeeId EmpName FloorId
1 def123 def 2
2 pqr123 pqr 2
You can use ROW_NUMBER(). It returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition.
Try this:
SELECT ROW_NUMBER() OVER(ORDER BY ID) as ID,EmployeeId,EmpName,FloorId
FROM tableName
WHERE FloorId=2
The result will be like:
ID EMPLOYEEID EMPNAME FLOORID
1 def123 def 2
2 pqr123 pqr 2
See result in SQL Fiddle.
Read more about ROW_NUMBER() here.
DECLARE #TableName TABLE
([ID] int, [EmployeeId] varchar(6), [EmpName] varchar(3), [FloorId] int)
;
INSERT INTO #TableName
([ID], [EmployeeId], [EmpName], [FloorId])
VALUES
(1, 'abc123', 'abc', 1),
(2, 'xyz123', 'xyz', 1),
(3, 'def123', 'def', 2),
(4, 'pqr123', 'pqr', 2)
;
;WITH CTE AS
(
select ID,EmployeeId,EmpName,ROW_NUMBER()OVER( ORDER BY ID ) RN,FloorId from #TableName
)
select * from CTE
WHERE FLOORid = 2

Query results as comma-delimited string without using PIVOT?

i want to create a query from multiple records as one record , but i don't want to use Pivot, FYI , i have > 260 Records , it is impossible if i Write Manual PIVOT,
is there any Simple solutions?
here's the table Example :
ID Element_Name Value
1 Parmitha 100
2 Anggun 200
3 Chandra 300
4 BagusofTerror 400
5 Laras 500
6 Jessica 600
7 Aisyah 700
......
200 Sonya 20000
and i want the result is like this :
paramitha , anggun, chandra , bagusofterror, Laras , Jessica , Aisyah, ..... , Sonya
100 , 200, 300, 400,500,600,700,....,20000
In reference to http://blog.sqlauthority.com/2009/12/21/sql-server-comma-separated-values-csv-from-table-column-part-2/ you could use the following SQL query
I havent run this, so I'm not 100% sure it'll work
SELECT STUFF(
(SELECT ',' + s.ElementName
FROM tableName s
FOR XML PATH('')),1,1,'')
UNION ALL
SELECT STUFF(
(SELECT ',' + s.Value
FROM tableName s
FOR XML PATH('')),1,1,'')
We used to call this a cross-tab query. Not sure if it still is...
SELECT SUM(CASE WHEN id=1 THEN value ELSE 0 END) AS parmitha,
SUM(CASE WHEN id=2 THEN value ELSE 0 END) AS anggun,
SUM(CASE WHEN id=3 THEN value ELSE 0 END) AS chandra,
...etc...
SUM(value) AS total
FROM My_Table
WHERE ...etc...
I don't know c#, but I'm sure you can get it to loop over the column to prepare the SUM().
How about this that I tried on the sample dataset you provided.
declare #intFlag int
Declare #AnsString nvarchar(2000)
SET #intFlag=1
SET #AnsString=''
While (#intFlag<=200)
Begin
Select #AnsString=#AnsString
+CONVERT(Varchar(20),(SELECT [Element_Name]
FROM TABLE where RowID=#intFlag))
+', '
SET #intFlag=#intFlag+1
if #intFlag=199
break;
END
Print #AnsString
--Select #AnsSTring
If you want this data in separate columns, then you can use dynamic SQL to PIVOT the data in SQL Server 2005+. Your code would be similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(element_name)
from yourtable
group by element_name, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select '+#cols+'
from
(
select Element_name, Value
from yourtable
) p
pivot
(
max(value)
for Element_name in('+#cols+')
) piv'
execute(#query)
See SQL Fiddle with Demo. This gives the result:
| PARMITHA | ANGGUN | CHANDRA | BAGUSOFTERROR | LARAS | JESSICA | AISYAH | SONYA |
----------------------------------------------------------------------------------
| 100 | 200 | 300 | 400 | 500 | 600 | 700 | 20000 |

Categories