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
Related
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 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
;
I have Invoice Numbers that are stored as nvarchar(25).
Their Format is ‘####AA’
Where #### is the Invoice Number and AA is the Version Number (partial Order Shipping)
I cannot change the format.
I created two Scalar Functions:
CREATE FUNCTION [dbo].[fnNumbersFromStr](#str varchar(8000))
RETURNS int
AS
BEGIN
WHILE PATINDEX('%[^0-9]%',#str)> 0
SET #str = REPLACE(#str, SUBSTRING(#str, PATINDEX('%[^0-9]%', #str), 1), '')
RETURN CAST(#str AS INT)
END
And it’s brother:
CREATE FUNCTION [dbo].[fnStringFromNum](#str varchar(25))
RETURNS varchar(25)
AS
BEGIN
WHILE PATINDEX('%[^a-z]%',#str)> 0
SET #str = REPLACE(#str, SUBSTRING(#str, PATINDEX('%[^a-z]%', #str), 1), '')
RETURN #str
END
I am stalled with this script:
SELECT
strInvoiceNo,
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
Which when runs returns:
strInvoiceNo InvoiceNumber InvoiceString
1000A 1000 A
1000B 1000 B
1000C 1000 C
1001A 1001 A
1001B 1001 B
1002AA 1002 AA
1002AB 1002 AB
1003A 1003 A
1004A 1004 A
I just can’t figure out the next step from here. I am stuck.
I would like the select to only return the latest Invoice Versions:
1000C
1001B
1002AB
1003A
1004A
Sql, Lamda or Linq will work fine for me.
Thanks in advance,
Try this:
SELECT
InvoiceNumber + MAX(InvoiceString) As strInvoiceNo
FROM
(
SELECT
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
) As tbl
GROUP BY InvoiceNumber
I dont think you need any UDF for this, a simple windowing function query should return what you looking for.
WITH x AS
(
Select *
,ROW_NUMBER() OVER (PARTITION BY InvoiceNumber ORDER BY strInvoiceNo DESC) rn
FROM TableName
)
SELECT strInvoiceNo, InvoiceNumber, InvoiceString
FROM X
WHERE rn = 1
OR
SELECT strInvoiceNo, InvoiceNumber, InvoiceString
FROM
(
Select *
,ROW_NUMBER() OVER (PARTITION BY InvoiceNumber ORDER BY strInvoiceNo DESC) rn
FROM TableName
)x
WHERE rn = 1
Here is it in LINQ (Assuming fnStringFromNum returns a string padded on the left with spaces):
dbContext.YOURTABLE
.GroupBy(x=>UDFFunctions.fnNumbersFromStr(x.AccountNumber))
.Select(x=>x.OrderByDescending(y=>UDFFunctions.fnStringFromNum(y.AccountNumber).FirstOrDefault())
SQL (using current fnStringFromNum):
SELECT
InvoiceNumber + LTRIM(MAX(RIGHT(SPACE(20)+InvoiceString,20))) As strInvoiceNo
FROM
(
SELECT
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
) As tbl
GROUP BY InvoiceNumber
Not necessarily the most efficient, but this will work:
select strInvoiceNo
from #TempTable T
where InvoiceString = (select max(invoicestring) from temp1 where invoicenumber = T.invoicenumber)
order by 1
Edit: Sorry....disregard. This will work off of your full result table but may not be what you actually need. Apologies.
My table is
(id int,property varchar)
Suppose I want to insert {3, 'a, b, c'}. How can I do it in a stored procedure?
The table entry would be:
id property
3 a
3 b
3 c
Also when i want to update my id=3 row by {3, 'ab, b, bg, ht'} . What would be the stored procedure for this insert and update operation? My table entries should become
id property
3 ab
3 b
3 bg
3 ht
sample table:
create table yourtable (id int,property varchar(5))
Procedure for that table:
create procedure p_test
(
#id int,
#prolist varchar(2000)
) as
begin
;with x as
(
SELECT * FROM yourtable WHERE id = #ID
)
MERGE INTO
x t1
using
(SELECT #id id, ltrim(t.c.value('.', 'VARCHAR(2000)')) property
FROM (
SELECT x = CAST('<t>' +
REPLACE(#prolist, ',', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)) t2 on t1.id = t2.id and t1.property = t2.property
when not matched then INSERT (id,property)
VALUES(t2.id, t2.property)
when matched
THEN UPDATE SET t1.id = t2.id
WHEN NOT MATCHED BY SOURCE THEN DELETE
;
end
Testing:
exec p_test 3, 'b,c'
select * from yourtable
exec p_test 3, 'a,b,c'
select * from yourtable
exec p_test 3, 'a,c'
select * from yourtable
exec p_test 4, 'g,h'
select * from yourtable
Result:
id property
3 b
3 c
id property
3 b
3 c
3 a
id property
3 c
3 a
id property
4 g
3 c
3 a
4 h
EDIT:
in order to update a new column use this table:
create table yourtable (id int,property varchar(5), is_active bit default 1)
Use this procedure:
alter procedure p_test
(
#id int,
#prolist varchar(2000)
) as
begin
;with x as
(
SELECT * FROM yourtable WHERE id = #ID
)
MERGE INTO
x t1
using
(SELECT #id id, ltrim(t.c.value('.', 'VARCHAR(2000)')) property
FROM (
SELECT x = CAST('<t>' +
REPLACE(#prolist, ',', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)) t2 on t1.id = t2.id and t1.property = t2.property
when not matched then INSERT (id,property, is_active)
VALUES(t2.id, t2.property, 1)
when matched
THEN UPDATE SET t1.id = t2.id, is_active = 1
WHEN NOT MATCHED BY SOURCE THEN
UPDATE SET t1.is_active = 0
;
end
First one:
insert into <tableName>(id, property) values(3, 'a');
insert into <tableName>(id, property) values(3, 'b');
insert into <tableName>(id, property) values(3, 'c');
Second issue:
update <tableName> set property='ab' where property = 'a';
update <tableName> set property='bg' where property = 'c';
insert into <tableName>(id, property) values(3, 'ht');
And now, a question: are you sure this is what your problem needs? Usually, when we call a column id we want it to be an identifier, that is, unique for each row. This may be a little bit off topic, but just in case...
There should be an id/primary key which will be unique. Profide any unique field and update records on the base of that unique field. Or you can make property unique or pair of id and property unique.
If I understood well, you could do something similar to this:
Insert INTO #tempTable
SELECT 3, * FROM dbo.splitstring('a,b,c,d')
It's just a pseudocode and you should do it inside your stored procedure.
If this is the right approach you have to take a look at this: T-SQL split string
Suppose I have following data in my table, where an Item has a parent ID 0. But there could only be three sub-levels/children of a parent. That is we need each item that has parentId = 0, and each parent can have up to three levels of children.
pkProductID Name ParentId
-------------------------------------------
1 Cloth 0
2 T-Shirts 1
3 Men-Shirts 2
4 Women-Shirts 2
5 Kids-Shirts 3
6 Cosmetics 0
7 Creams 6
8 Men-cream 7
9 Women-Cream 7
10 Kids-cream 9
We need list that contains items that has parentID=0 and then 2 levels of that children that means from above I need only these items:
1 Cloth
2 T-Shirts
3 Men-Shirts
4 Women-Shirts
6 Cosmetics
7 Creams
8 Men-Cream
9 Women-Cream
The following code should do the work (change table or columns names accordingly):
use [DemoDB]
GO
WITH Emp_CTE AS (
SELECT ProductId, Name, ParentID, 0 as [Level]
FROM [dbo].[Products]
WHERE ParentId = 0
UNION ALL
SELECT p.ProductId, p.Name, p.ParentID, [Level] + 1
FROM [dbo].[Products] as p
INNER JOIN Emp_CTE ecte ON ecte.ProductId = p.ParentID
WHERE [Level] < 2
),
CTE2 AS
(
SELECT [dbo].[Products].*
FROM Emp_CTE
INNER JOIN [dbo].[Products] ON Emp_CTE.ProductId = [dbo].[Products].ParentID
)
SELECT * FROM CTE2
GO
You can use a recursive CTE for this type of task, take a look at this link here that shows a simple example: http://blog.sqlauthority.com/2008/07/28/sql-server-simple-example-of-recursive-cte/ and there's another example here http://msdn.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx
CREATE TABLE #clothes(
pkProductID INT,
NAME VARCHAR(50),
ParentId INT
)
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 1, 'cloth', 0 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 2, 'tshirt', 1 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 3, 'mens tshirt', 2 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 4, 'womens tshirt', 2 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 5, 'kids tshirt', 3 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 6, 'cosme', 0 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 7, 'cream', 6 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 8, 'm cream', 7 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 9, 'w cream', 7 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 10, 'kids cream', 9 )
WITH myCTE AS (
SELECT pkProductID ,
NAME ,
ParentId, 0 AS ItemLevel FROM #clothes
WHERE ParentId = 0
UNION all
SELECT c.pkProductID ,
c.NAME ,
c.ParentId, ItemLevel + 1 FROM #clothes c INNER JOIN myCTE mc ON mc.pkProductID = c.ParentId
)
SELECT * FROM myCTE WHERE ItemLevel <=2 ORDER BY pkProductID