Need Query - Row count with loop - sql

I have a Primary table like
========================================
ID NAME logtime (date time colum)
========================================
1 cat dd/mm/yyyy 10.30
2 cat dd/mm/yyyy 9.20
3 cat dd/mm/yyyy 9.30
4 cat dd/mm/yyyy 7.20
Secondary Table like
---------------------
Name improvement
---------------------
cat 1
Now I want to create a loop
To calculate difference between first 2 rows, if the difference is >= 1 hr then update the secondary table as existing value +1(that is 2) else rest it.
Now calculate 2'nd and 3'rd rows,
To calculate difference between first 2 rows, if the difference is >= 1 hr then update the secondary table as existing value +1, here the answer is 2.
Then 3'rd and 4'th.
Once all row are calculated then exit from the loop.
can anyone give me a query for this?

declare #timediff table(id int identity(1,1),Name varchar(50),logtime datetime)
insert into #timediff Select Name,logtime from timediff
declare #datetimeDiff int
declare #i int=1
while(#i<=(Select count(*) from #timediff))
Begin
Select #datetimeDiff=datediff(hour,logtime,
(Select logtime from #timediff where id=#i+1))
from #timediff where id=#i
if(#datetimeDiff>=1)
BEGIN
Select #datetimeDiff
--You can write your update code here
END
Set #i=#i+2
END

Related

Only begin stored procdure if previous value is less than new value being inserted into table SQL Server

I have a stored procedure. I only want it to insert new values if the date value is higher than previous record.
Department Change Date
------------------------------------
Catering 3 2018-01-01 08:09:00.000 - Current Record
Department Change Date
-----------------------------------------------
Catering 3 2018-01-02 09:10:00.000 - New record to be inserted
I have this so far, but obviously the select is wrong. I want to do something like this before the Insert statement
if select top 1 *
from database.[dbo].[DepartmentChange]
where Department = #Department
and Date > #Date
order by Date desc
begin
please help
Pretty vague details here but I think you are looking for something like this.
if not EXISTS
(
select *
from database.[dbo].[DepartmentChange]
where Department = #Department
and Date > #Date
)
begin
--do your insert
end
else
begin
--do something else here
end
Vague question. I'm assuming #Date, #Department are from the new record. So maybe like this . . .
if #Date > (select isnull(max(date),'1500-01-01') from database.[dbo].[DepartmentChange]
where Department = #Department)
begin
insert statement here
end

in a table column row by row subtraction to find the value in SQL Server

id patient_date
1 10/5/2017
2 6/6/2017
3 6/10/2017
4 8/7/2017
5 9/19/2017
Output:
id patient_date days
1 10/5/2017 (6/6/2017-10/5/2017)
2 6/6/2017 (6/10/2017-6/6/2017)
3 6/10/2017 (8/7/2017-6/10/2017)
4 8/7/2017 (9/19/2017-8/7/2017)
5 9/19/2017
Here's query with extra column for you to choose :)
declare #Table table(ID int identity(1,1), patient_date date)
insert into #Table values
('10/5/2017'),
('6/6/2017'),
('6/10/2017'),
('8/7/2017'),
('9/19/2017')
select A.ID,
A.patient_date,
cast(B.patient_date as varchar(10)) + ' - ' + cast(A.patient_date as varchar(10)) as Period, --this column will show exactly what you asked
abs(datediff(day,B.patient_date, A.patient_date)) as DaysDifference --this column is computed absolute difference in days between to dates
from #Table A left join #Table B on A.ID = B.ID - 1
You can try this. This will use lead to find your next value. The last value should either be null or nothing, correct it as you need.
The date 1900-01-01 should be changed to your desired wishes. It could also be NULL as value. Then it wont calculate the last row.
DECLARE #table TABLE (ID int,Patient_date date)
INSERT INTO #table VALUES
(1, '10/5/2017'),
(2,'6/6/2017'),
(3,'6/10/2017'),
(4,'8/7/2017'),
(5,'9/19/2017')
select *,DATEDIFF(DD,Patient_date,NextDate) as DaysBetween,
'('+cast(Patient_date as varchar(50)) + ' - ' + cast(NextDate as
varchar(50))+')' as DayString from (
select *,LEAD(Patient_date,1,'1900-01-01') over(order by ID ) as NextDate
from #table
) x
In my result i used NULL instead of 1900-01-01 - Also notice i use another date format than you, but it shouldnt be a problem.
Result:

Creating Stored Procedure to compare Date Column and update Status Column

I need a stored procedure to be created for comparing EXPIRYDATE column with SYSTEMDATE and if they match equal, then an additional column called MATCHSTATUS must be updated with value 1 and if they don't match then it must be update with value 0.
Both EXPIRYDATE and MATCHSTATUS are present in the same table.
Example:
username password expirydate matchstatus
-----------------------------------------------------
abc 987 01-Feb-2015 1
xyz 678 10-Feb-2015 0
ghi 456 15-Jan-2015 0
In this example, the match status column should be update by comparing expirydate column with sysdate and it should be done by a stored procedure.
Code can be something like that
Declare #crntdt NVARCHAR(20)
Declare #mydt NVARCHAR(20)
Select
#crntdt = REPLACE(rtrim(ltrim(CONVERT(CHAR(15), getdate(), 106))),' ',' - ')
--select #crntdt
select #mydt=expirydate from your_table
if #crntdt=#mydt
begin
update your_table set matchstatus=1 where expirydate=#mydt
end
This is what you need I think - this assumes expirtydate is a date, if it's varchar you will need to cast it as a date:
CREATE PROCEDURE sp_name
as
Begin
Update tbl
set matchstatus = case when expirydate = cast(GETDATE() as date) then 1 else 0 end
END

Create a random selection weighted on number of points, SQL

I have a table of winners for a prize draw, where each winner has earned a number of points over the year. There are 1300 registered users, with points varying between 50 and 43,000. I need to be able to select a random winner, which is straight forward, but the challenge I am having is building the logic where each point counts as an entry ticket into the prize draw. Would appreciate any help.
John
Your script would look something similar to this:
Script 1 :
DECLARE #Name varchar(100),
#Points int,
#i int
DECLARE Cursor1 CURSOR FOR SELECT Name, Points FROM Table1
OPEN Cursor1
FETCH NEXT FROM Cursor1
INTO #Name, #Points
WHILE ##FETCH_STATUS = 0
BEGIN
SET #i = 0
WHILE #i < #Points
BEGIN
INSERT INTO Table2 (Name)
VALUES (#Name)
SET #i = #i + 1
END
FETCH NEXT FROM Cursor1 INTO #Name, #Points
END
DEALLOCATE Cursor1
I have created a table (Table1) with only a Name and Points column (varchar(100) and int), I have created a cursor in order to look through all the records within Table1 and then loop through the Points and then inserted each record into another table (Table2).
This then imports the Name depending on the Points column.
Script 2 :
DECLARE #Name varchar(100),
#Points int,
#i int,
#Count int
CREATE TABLE #temptable(
UserEmailID nvarchar(200),
Points int)
DECLARE Cursor1 CURSOR FOR SELECT UserEmailID, Points FROM Table1_TEST
OPEN Cursor1
FETCH NEXT FROM Cursor1
INTO #Name, #Points
WHILE ##FETCH_STATUS = 0
BEGIN
SET #i = 0
WHILE #i < #Points
BEGIN
INSERT INTO #temptable (UserEmailID, Points)
VALUES (#Name, #Points)
SET #i = #i + 1
END
FETCH NEXT FROM Cursor1 INTO #Name, #Points
END
DEALLOCATE Cursor1
SELECT * FROM #temptable
DROP TABLE #temptable
In Script2 I have imported the result into a TEMP table as requested.
The script now runs through each record within you Table1 and imports the individuals UserEmailID and Points into the TEMP table depending on how much the Points are in Table1.
So if John has a total of 3 points, and Sarah 2, the script will import Johns UserEmailID 3 times into the TEMP table and 2 times for Sarah.
If you apply the random selector on the TEMP table, it will then randomly select a individual.
John would obviously stand a better chance to win because he has 3 records in the TEMP table whereas Sarah only has 2.
Suppose Johns UserEmailID is 1 and Sarah is 2:
The OUTPUT of TEMP table would then be:
UserEmailID | Points
1 | 3
1 | 3
1 | 3
2 | 2
2 | 2
Please let me know if you need any clarity.
Hope this helps.
You can do a weighted draw using the following method:
Calculate the cumulative sum of points.
Divide by the total number of points to get a value between 0 and 1
Each row in the original data will have a range, such as [0, 0.1), [0.1, 0.3), [0.3, 1]
Calculate a random number and choose the row where the value falls in the range
Here is standard'ish SQL for this approach:
with u as (
select u.*,
coalesce(lead(rangestart) over (order by points) as rangeend, 1)
from (select u.*,
sum(points*1.0) over (order by points) / sum(points) over () as rangestart
from users u
) u
),
r as (
select random() as rand
)
select u.*
from u
where r.rand between rangestart and rangeend;
In addition to using window functions (which can be handled by correlated subqueries in many cases), the exact format depends on whether the random number generator is deterministic for a query (such as SQL Server where random() returns one value no matter how often called in a query) or non-deterministic (such as in other databases). This method only requires one value for the random number generator, so it will work with either method.
So you want a winner with 1000 points have double the chances as another with only 500 points.
Sort the winners by whatever order and create a running total for the points:
id points
winner1 100
winner2 50
winner3 150
gives:
id points from to
winner1 100 1 100
winner2 50 101 150
winner3 150 151 300
Then compare with a random number from 1 to sum(points), in the example a number between 1 and 300. Find the winner with that number range and you're done.
select winpoints.id_winner
from
(
select
id as id_winner,
coalesce(sum(points) over(order by id rows between unbounded preceding and 1 preceding), 0) + 1 as from_points,
sum(points) over(order by id rows between unbounded preceding and current row) as to_points
from winners
) winpoints
where (select floor(rand() * (sum(points) from winners)) + 1)
between winpoints.from_points and winpoints.to_points;
This solution also works with fractional points/weights. It creates a helper table usersum.
create table user (id int primary key, points float);
insert into user values (1, 0.5), (2, 0), (3, 1);
create table usersum (id int primary key, pointsum float);
insert into usersum
select id, (select sum(points) from user b where b.id <= a.id)
from user a;
set #r = rand() * (select max(pointsum) from usersum);
select #r, usersum.* from usersum where pointsum >= #r order by id limit 1;
http://sqlfiddle.com/#!2/ae539e/1

Sorting results of SQL query just like IN parameter list

I'm doing a query which looks something like
SELECT id,name FROM table WHERE id IN (2,1,4,3)
I'd like to get
id name
2 B
1 A
4 D
3 C
but I'm getting
1 A
2 B
3 C
4 D
Is there any way to sort the query results in the same way as the list I'm including after IN?
Believe me, I have a practical reason that I would need it for ;)
SELECT id,name FROM table WHERE id IN (2,1,4,3)
ORDER BY CASE id
WHEN 2 THEN 1
WHEN 1 THEN 2
WHEN 4 THEN 3
WHEN 3 THEN 4
ELSE 5
END
This might solve your problem.
Solution 2, insert your list into a temp table and get them a running sequence
id, seq(+1 every new row added)
-----------------
2 1
1 2
4 3
3 4
then join 2 table together and order by this seq.
Okay, I did it myself. It's a bit mad but it works ;)
DECLARE #IDs varchar(max)
DECLARE #nr int
DECLARE #znak varchar(1)
DECLARE #index int
DECLARE #ID varchar(max)
SET #IDs='7002,7001,7004,7003'
SET #nr=1
SET #index=1
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
CREATE TABLE #temp (nr int, id int)
--fill temp table with Ids
WHILE #index<=LEN(#Ids)
BEGIN
set #znak=''
set #ID=''
WHILE #znak<>',' AND #index<=LEN(#Ids)
BEGIN
SET #znak= SUBSTRING(#IDs,#index,1)
IF #znak<>',' SET #ID=#ID+#znak
SET #index=#index+1
END
INSERT INTO #temp(nr,id) VALUES (#nr,CAST(#ID as int))
SET #nr=#nr+1
END
-- select proper data in wanted order
SELECT MyTable.* FROM MyTable
INNER JOIN #temp ON MyTable.id=#temp.id
ORDER BY #temp.nr