Translate a Linq query into a database View - c#

I have a table TestPack in my database and this table has 1-N relationship with table Documentation and InternalWalk. Below code compiles Test Pack status report by fetching all Test Packs from database and iterating over each of them the report gets compiled. My application is architectured in a way that Database is hosted on a Windows Server machine (SQL Server) and client applications run over the network. So the below code first gets list of all TestPacks and in each loop iteration it again queries the Documentation and InternalWalk tables. This slows down things a lot.
var AllTestPacks = Project.GetAllTestPacksFromDB();
foreach (var tp in AllTestPacks)
{
var testPack = new TestPackStatus { TestPackNo = tp.test_pack_no };
var documentation = tp.Documentations.LastOrDefault();
if (documentation != null && documentation.status == "Accepted" && documentation.acceptance_date >= cutOffDate)
testPack.Documentation = documentation.ReadinessDate;
var internalWalk = tp.InternalWalks.LastOrDefault();
if (internalWalk != null && internalWalk.status == "Accepted" && internalWalk.acceptance_date >= cutOffDate)
testPack.InterWalks = internalWalk.AcceptanceDate;
StatusData.Add(testPack);
}
I was thinking if I can translate the below code into a database View and I simply fetch the data from the View, that would speed things up significantly. How can I can translate the above code into a View.
Here is what I have tried so far but I am unable to implement this LastOrDefault() thing.
SELECT dbo.TestPack.test_pack_no, dbo.Documentation.rfi_no, dbo.Documentation.rfi_date, dbo.Documentation.status,
FROM dbo.TestPack INNER JOIN dbo.Documentation ON dbo.TestPack.id = dbo.Documentation.test_pack_id WHERE (dbo.Documentation.status = 'Accepted')

Assuming you want to join onto the "latest" Documentation or InternalWalk record by test_pack_no and sorting by acceptance_date to distinguish the latest record, the following SQL (excluding the table variables and replaced with your own tables) could be placed into a stored procedure passing #cutoff as a parameter:
declare #TestPack table (test_pack_no int)
insert into #TestPack values (1)
insert into #TestPack values (2)
declare #Documentation table (test_pack_no int, Name varchar(20), status varchar(20), acceptance_date datetime)
insert into #Documentation values (1, 'Doc 1', 'Accepted', '21 Dec 2015')
insert into #Documentation values (1, 'Doc 2', 'Accepted', '22 Dec 2015')
insert into #Documentation values (2, 'Doc 1', 'Non-Accepted', '22 Dec 2015')
insert into #Documentation values (2, 'Doc 2', 'Accepted', '21 Dec 2015')
declare #InternalWalk table (test_pack_no int, Name varchar(20), status varchar(20), acceptance_date datetime)
insert into #InternalWalk values (1, 'Walk 1', 'Accepted', '20 Dec 2015')
insert into #InternalWalk values (1, 'Walk 2', 'Accepted', '21 Dec 2015')
declare #cutoff datetime = '21 dec 2015'
;with cte_Documentation
as
(
select
row_number() over (partition by test_pack_no order by acceptance_date desc) [RN], *
from
#Documentation
where
status='Accepted'
and acceptance_date >= #cutoff
),
cte_InternalWalk
as
(
select
row_number() over (partition by test_pack_no order by acceptance_date desc) [RN], *
from
#InternalWalk
where
status='Accepted'
and acceptance_date >= #cutoff
),
cte_TestPack
as
(
select
tp.*, d.status, d.Name [Doc_Name], d.acceptance_date [Doc_AcceptDate], iw.Name [InternalWalk_Name], iw.acceptance_date [InternalkWalk_AcceptDate]
from
#TestPack tp
left join cte_Documentation d on d.test_pack_no=tp.test_pack_no and d.RN=1
left join cte_InternalWalk iw on iw.test_pack_no=tp.test_pack_no and iw.RN=1
)
select * from cte_TestPack
It was unclear from your example exactly what columns were filtered and needed as part of your result.
You simply have EF call the SPROC passing the cut-off date as a parameter which will return a flat-structure which can then be converted into your individual objects TestPack, Documentation and InternalWalk

Related

Given a sorted row of n numbers in SQL server, how do i find at what point the sum of the rows reach the value k?

Assume i got the below row of number and max quantity value is 10.
Quantity BatchValue
2 0
4 0
4 0
6 1
8 2
Summation of 2+4+4 gives me a value less than or equal to max quatity 10 and so the batch value for those rows become 0. The pending rows are 6 and 8. They cannot be summed up to be < max quantity. So they will be seperate. Can we get an sql query or an algorith that can do this?
Here's a nice running sum routine you can use
create table #temp (rowid int identity, quantity int)
insert #temp
select quantity from yourtable order by your order
declare #holding table (quantity int, runningsum int)
declare #quantity int
declare #running int=0
declare #iterator int = 1
while #iterator<=(select max(rowid) from #temp)
begin
select #quantity=quantity from #temp where rowid=#iterator
set #running=#quantity+#running
insert #holding
select #quantity, #running
set #iterator=#iterator+1
end
Edited code from Daniel Marcus above to give the actual response requested in query.
CREATE TABLE #temp(rowid int identity(1,1), quantity int)
INSERT INTO #temp
SELECT 2
UNION ALL SELECT 4
UNION ALL SELECT 4
UNION ALL SELECT 6
UNION ALL SELECT 8
declare #batchValue int = 0 ,#maxquantity int = 10
declare #holding table (quantity int, batchvalue int)
declare #quantity int
declare #running int=0
declare #iterator int = 1
while #iterator<=(select max(rowid) from #temp)
begin
select #quantity=quantity from #temp where rowid=#iterator
set #running=#quantity+#running
-- Newly added condition
if (#running > #maxquantity) BEGIN
SET #batchValue = #batchValue + 1 -- increment the batch value
insert #holding select #quantity, #batchValue
SET #running = #quantity -- reset the running value
END
ELSE
insert #holding select #quantity, #batchValue
set #iterator=#iterator+1
end
SELECT * FROM #holding
DROP TABLE #temp
Hope the snippet works for your purpose. I tested this in SQL azure and provides the result you mentioned.
--The below query will help you if you working on sql server 2012 or higher
CREATE TABLE #RUN_TOT(ID INT)
INSERT INTO #RUN_TOT VALUES(2),(4),(4),(6),(8)
;WITH CTE AS
(
SELECT ID,
ROW_NUMBER() OVER(ORDER BY ID) RNUM,
CASE
WHEN SUM(ID) OVER(ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) <=
(SELECT MAX(ID) FROM #RUN_TOT) THEN 0
ELSE
ID
END VAL
FROM #RUN_TOT
)
SELECT ID,VAL FROM CTE WHERE VAL=0
UNION ALL
SELECT ID,ROW_NUMBER() OVER(ORDER BY VAL) VAL FROM CTE WHERE VAL<>0

How to convert SQL table into a customised gridview

I have a c# web page and i am using SQL for the database
i need to display the data from table into a gridview
SQL : I have a table as below
Exec_Date Plat Pass Fail
----------------------------------------------------------------
2017-02-19 12:32:43.570 MSSQL 10 12
2017-02-19 12:32:43.570 MSSQL 10 12
2017-02-18 12:32:43.570 Sybase 10 12
2017-02-18 12:32:43.570 Oracle 10 12
i would like to convert the table into a customised format for a presentation
Status 18/02 19/02
-------------------------
Pass 20 20
Fail 24 24
is the above possible to be done by Pivot ? if so can anyone give some idea on that pls?
I have tried the below so far and the #Names is not resolving the variable value in query
create table #TempTable
(Exec_Date varchar(max),
Pass int,
Ref_Status varchar(max)
)
Insert into #TempTable
select cast(Exec_Date as DATE) as Date,SUM(Pass) as Pass,'PASS' from F_Exec where cast(Exec_Date as DATE) >= '2017-02-17' and cast(Exec_Date as DATE) <= '2017-02-20' group by Exec_Date,Pass
DECLARE #Names varchar(max)
SELECT #Names = COALESCE(#Names + '],[', '[') + Exec_Date FROM #TempTable
select #Names=#Names+']'
Select * from #TempTable pivot
(SUM(Pass) for Exec_Date IN (#Names) )as PivotTable
The variable #Names is not considered as variable. If i manually replace that resultant value from the variable it works
Try this code:
declare #tbl table (Exec_Date datetime, Plat varchar(max), Pass int, Fail int)
insert into #tbl(Exec_Date , Plat , Pass , Fail ) values
('2017-02-19 12:32:43.570', 'MSSQL',10,12),
('2017-02-19 12:32:43.570', 'MSSQL',10,12),
('2017-02-18 12:32:43.570', 'Sybase',10,12),
('2017-02-18 12:32:43.570', 'Oracle',10,12)
select * from
(
select Date,Status,sum(Success) success from
(
select left(convert(varchar(20),Exec_Date,103),5) as [Date],Pass,Fail from #tbl
) as baseData
unpivot
(
Success for Status in (Pass,Fail)
) as tblUnPivot
group by date ,status
) as baseData
pivot
(
sum(success)
for Date in ([18/02],[19/02])
) as tblPivot
order by Status
I got the solution
declared nvarchar variable and concatenated the query & dynamic column names
DECLARE #sqlstring nvarchar(MAX)
SET #sqlstring =N'
Select * from #TempTable pivot (SUM(Pass) for Exec_Date IN (' +#Names+') )as PivotTable;';
EXEC sp_executesql #sqlstring;

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).

Return row_number() of affected rows after an UPDATE statement

In my C# application I'm executing multiple update queries to manipulate data in the database table. E.g. replace a specific character set into a different character set, insert new characters and remove characters. When a query like this has executed I want to do two things. Get the total rowcount of the affected rows and get a row_number() result set of the affected rows. The first thing is quite simple and is working already. The second thing however is something I haven't been able to figure out yet.
Here is an example of a query that I might use when I'm manipulating data:
UPDATE myTable
SET myColumn = STUFF(myColumn, fromCharPos, toCharPos,
REPLACE(SUBSTRING(myColumn, fromCharPos, toCharPos), charToReplace, charReplacement))
WHERE LEN(myColumn) >= fromCharPos;
This query replaces (on all the cells of a column) a character set with another character set within a specified character range.
When this query has executed I want to get a result set of row numbers from the affected rows. Anyone know how I'm able to implement this?
Some things to consider:
It has to work on atleast SERVER version 2005 and up.
The UPDATE statements are executed within a transaction
If anything is unclear, please comment below so I'm able to improve my question.
Edit:
I noticed that it is not quite clear what I want to achieve.
Lets say we have a set of data that looks like this:
34.56.44.12.33
32.44.68
45.22.66.33.77
44.42.44
66.44.22.44.45
00.22.78
43.98.34.65.33
Now I want to replace the dots with an underscore between character position 9 to 12. That means that only these rows will be affected by the query:
34.56.44.12.33 <--
32.44.68
45.22.66.33.77 <--
44.42.44
66.44.22.44.45 <--
00.22.78
43.98.34.65.33 <--
The thing I want to achieve is to get a row number result set of the affected rows. In my example that will be a result set like this:
Row_number()
1
3
5
7
This may help you..
CREATE TABLE #updatetablename
(excolumn VARCHAR(100))
INSERT INTO #updatetablename
VALUES ('34.56.44.12.33'),
('32.44.68'),
('45.22.66.33.77'),
('44.42.44'),
('66.44.22.44.45'),
('00.22.78'),
('43.98.34.65.33')
DECLARE #temp TABLE
(excolumn VARCHAR(100))
DECLARE #temp1 TABLE
(row_num INT,excolumn VARCHAR(100))
INSERT INTO #temp1
SELECT Row_number()OVER (ORDER BY excolumn),*
FROM #updatetablename
UPDATE #updatetablename
SET excolumn = Replace(excolumn, '.', '_')
output deleted.excolumn
INTO #temp
WHERE Len(excolumn) > 12
SELECT b.row_num AS updatedrows,
a.excolumn
FROM #temp a
JOIN #temp1 b
ON a.excolumn = b.excolumn
Updated
declare #table table(val varchar(500))
insert into #table values
('34.56.44.12.33'),
('32.44.68'),
('45.22.66.33.77'),
('44.42.44'),
('66.44.22.44.45'),
('00.22.78'),
('43.98.34.65.33')
--select * from #table
declare #temp table(rowid int,val varchar(500), createdate datetime)
insert into #temp
select ROW_NUMBER () over(order by val), val, GETDATE() from #table
declare #rowEffectedCount int = 0
--select ROW_NUMBER () over(order by val), val, GETDATE() from #table WHERE CHARINDEX( '.',val,9 ) > 0
UPDATE #table
SET val =
REPLACE(SUBSTRING(val, CHARINDEX( '.',val,9 ), LEN(val)), ',', '_' )
WHERE CHARINDEX( '.',val,9 ) > 0
set #rowEffectedCount = ##ROWCOUNT
select #rowEffectedCount roweffected ,* from #temp t1
where val not in (
select val from #table )
Old one
Its quite simple as my understanding.
You just add a one select query of your update query. read the comment for more understand
declare #rowEffectedCount int = 0
--you can use a temp table or permanet history table to take each work of portion.
--one thing to be carefull as structure is same or only save the pk , then no issue
--create table tempRowcount and insert the statement with the same where query, which filter the same data.
declare #t table(id int, createdate datetime)
select * into #t from myTable WHERE LEN(myColumn) >= fromCharPos
--or only pk
select id into #t from myTable WHERE LEN(myColumn) >= fromCharPos
--now update the
UPDATE myTable
SET myColumn = STUFF(myColumn, fromCharPos, toCharPos,
REPLACE(SUBSTRING(myColumn, fromCharPos, toCharPos), charToReplace, charReplacement))
WHERE LEN(myColumn) >= fromCharPos;
select * from #t
--finally delete or truncate or drop the table(if you use permanent table)

Sql Query for unique record based on largest number

I have a gridview that is currently displaying as you see in the top picture. I want it to display only the record that has the Largest Time as you see in picture two. For example, for Q1 largest time is 15 min, so only display Q1 15min, etc. This is dynamic so letters and numbers may change. Does anybody know a good sql server query for this?
UPDATED
Will this work for you?
Select distinct Number, MAX(Time) from MyTable group by Number
after comments:
create table #example(number varchar(50), id int, [time] nvarchar(50), descr nvarchar(50))
insert into #example (number, id, [time], descr) values ('Q1', 1, '10 Min', 'Shoe')
insert into #example (number, id, [time], descr) values ('Q1', 1, '15 Min', 'Car')
insert into #example (number, id, [time], descr) values ('Q1', 1, '6 Min', 'Shirt')
insert into #example (number, id, [time], descr) values ('L2', 1, '10 Min', 'Shoe')
insert into #example (number, id, [time], descr) values ('P3', 2, ' 8 Min', 'Garage')
insert into #example (number, id, [time], descr) values ('P3', 2, ' 3 Min', 'Plant')
insert into #example (number, id, [time], descr) values ('P3', 2, '20 Min', 'Tree')
SELECT t1.*
FROM #example AS t1
LEFT OUTER JOIN #example AS t2
ON (t1.number = t2.number AND t1.[time] < t2.[time])
WHERE t2.number IS NULL;
--In other words: fetch the row from t1 where no other row exists with the same Number and a greater Time.
I'm not sure if this is what you're looking for, but check it out:
create table #test (name varchar(10), number int)
insert into #test values ('a', 1)
insert into #test values ('a', 2)
insert into #test values ('a', 3)
insert into #test values ('b', 1)
insert into #test values ('b', 2)
insert into #test values ('b', 4)
select name, max(number) from #test group by name
Result:
name (No column name)
a 3
b 4

Categories