SQL SERVER generating row number based on criteria - sql-server-2008

I have a table which has columns UserID,DIFFTIME. when I select these columns from the table, I also want to have a derived column which is : If the DiffTime is > 20 I want to increment the count per user id.
For example if the table has:
User ID DIFF TIME
1 0
1 5
1 10
2 0
2 21
2 5
I want a result set that is something like this:
User ID DIFF TIME SESSION NUMBER
1 0 1
1 5 1
1 10 1
2 0 1
2 21 2
2 5 2
How do I accomplish this.
Ideas and suggestions are much appreciated!

Use this statement:
select t1.User_Id, t1.Diff_Time,
isnull(count(t2.User_Id), 0) + 1 as Session_Number
from #table t1
left join #table t2
on t1.User_Id = t2.User_Id
and t1.eventTime >= t2.eventTime
and t2.Diff_Time > 20
group by t1.User_Id, t1.Diff_Time, t1.eventTime
order by t1.User_Id, t1.eventTime
(replace #table with your actual table name)
Note: I assume that the fifth row of your table has the value 21 in the Diff_Time column, and there's a typo in the question, as #AaronBertrand pointed out in the comments

create table #t
(
id int,
Diff int,
SessionNumber int
)
insert into #t(id, diff)values(1, 0)
insert into #t(id, diff)values(1, 5)
insert into #t(id, diff)values(1, 10)
insert into #t(id, diff)values(2, 0)
insert into #t(id, diff)values(2, 21)
insert into #t(id, diff)values(2, 5)
Select ROW_NUMBER() over(order by Id) as RowID, * into #Temp1 from #t
Declare #diff int
Declare #RowId int
Declare #Previous int
Declare #NewValue int
DECLARE #Cur CURSOR SET #Cur = CURSOR FOR select RowId, diff from #Temp1
OPEN #Cur
FETCH NEXT FROM #Cur INTO #RowId, #diff
WHILE ##FETCH_STATUS = 0
BEGIN
if(#RowId = 1)
Begin
Update #Temp1 Set SessionNumber = 1 Where rowid = 1
Set #Previous = #Diff
Set #NewValue = 1
End
Else
Begin
if(#Diff - #Previous > 20)
Begin
Set #Previous = #Diff
Set #NewValue = #NewValue + 1
Update #Temp1 Set SessionNumber = #NewValue Where rowid = #RowId
End
else
Update #Temp1 Set SessionNumber = #NewValue Where rowid = #RowId
End
FETCH NEXT FROM #Cur INTO #RowId, #diff
END
CLOSE #Cur
DEALLOCATE #Cur
select * from #temp1
drop table #t
drop table #temp1

Related

Stored procedure is too slow in mysql

I have a routine. But it' s too slow. How can I improve the query?
My records: http://www.sqlfiddle.com/#!9/14cceb/1/0
My query:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE example.ssa()
BEGIN
drop table if exists gps_table;
drop table if exists exam_datas;
CREATE TEMPORARY TABLE gps_table(ID int PRIMARY KEY AUTO_INCREMENT,timei
int,
trun_date_time datetime, tadd_meter int, tin_here int null);
insert into gps_table(timei,trun_date_time,tadd_meter,tin_here) select
imei, run_date_time, add_meter, in_here from example_table;
CREATE TEMPORARY TABLE exam_datas(ID int PRIMARY KEY AUTO_INCREMENT,vimei
int, vbas_run_date_time datetime, vbit_run_date_time datetime, vdifff int);
select tin_here from gps_table limit 1 into #onceki_durum;
select count(id) from gps_table into #kayit_sayisi;
set #i = 1;
set #min_mes = 0;
set #max_mes = 0;
set #frst_id = 0;
set #imei = 0;
set #run_date_time = '0000-00-00 00:00:00';
set #run_date_time2 = '0000-00-00 00:00:00';
myloop: WHILE (#i <= #kayit_sayisi) DO
select tin_here from gps_table where id = #i into #in_here_true;
if (#in_here_true = 1) then
select id,trun_date_time, tadd_meter from gps_table where id = #i into #frst_id,#run_date_time2, #min_mes;
select id from gps_table where id > #frst_id and tin_here =0 order by id asc limit 1 INTO #id;
SET #id = #id-1;
select id, timei, trun_date_time, tadd_meter from gps_table
where id = #id and tin_here =1 limit 1 into #i, #imei, #run_date_time, #max_mes;
if(#i-#frst_id>3) then
set #i:=#i+1;
insert into exam_datas(vimei,vbas_run_date_time,vbit_run_date_time,vdifff) Values (#imei, #run_date_time2, #run_date_time, #max_mes-#min_mes);
SELECT * FROM exam_datas;
SET #asd =1;
elseif 1=1 then
set #i:=#i+1;
End if;
ELSEIF 1=1
THEN SET #i:=#i+1;
End if;
IF (#i = #kayit_sayisi)
THEN set #tamam =1; LEAVE myloop;
END IF;
END WHILE myloop;
select DISTINCT * from exam_datas;
drop table if exists exam_datas;
drop table if exists gps_table;
END
I need: id= 6 first true and id= 11 last_true
firs_trure - last_true = 304-290= 14
id=14 first true and id=18 last_true
firs_true - last_true = 332-324= 8
This routine is too slow.
MySql version is 5.7 and There are 2 milions record in the table.
UPDATE:
Query is here. HERE
Thank you #LukStorms
It's possible to get such results in 1 query.
Thus avoiding a WHILE loop over records.
This example works without using window functions. Just using variables inside the query to calculate a rank. Which is then used to get the minimums and maximums of the groups.
select
imei,
min(run_date_time) as start_dt,
max(run_date_time) as stop_dt,
max(add_meter) - min(add_meter) as diff
from
(
select imei, id, run_date_time, add_meter, in_here,
case
when #prev_imei = imei and #prev_ih = in_here then #rnk
when #rnk := #rnk + 1 then #rnk
end as rnk,
#prev_imei := imei as prev_imei,
#prev_ih := in_here as prev_ih
from example_table t
cross join (select #rnk := 0, #prev_ih := null, #prev_imei := null) vars
order by imei, id, run_date_time
) q
where in_here = 1
group by imei, rnk
having count(*) > 4
order by imei, min(id);
In the procedure such query can be used to fill that final temporary table.
A test on db<>fiddle here

Mysql group_concat limit rows in grouping

The next example is my database.
tb_port
id port
1 80
2 22
3 53
4 3128
5 443
tb_dest
id dest
1 network
2 local
tb_rule
id id_port id_dest
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
Select:
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
Result:
network 80,22,53,3128,443
but is not the result I'm looking for, the result would be this.
Select ex:
select dest,group_concat(port limit 2) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
result I would like
network 80,22
network 53,3128
network 443
how to achieve this result only with SQL?
Sqlfiddle: http://sqlfiddle.com/#!2/d11807
MySQL doesn't make this kind of query easy, but one (admittedly not very pretty) solution is to use a variable to give each row a sequence number per dest and just group by the row number integer divided by 2 to get two numbers in each group;
SELECT dest, GROUP_CONCAT(port ORDER BY rank) ports
FROM (
SELECT dest, port, (
CASE dest WHEN #curDest
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curDest := dest END) rank
FROM tb_port a
JOIN tb_rule c ON a.id = c.id_port
JOIN tb_dest b ON b.id = c.id_dest,
(SELECT #curRow := 0, #curDest := '') r
ORDER BY dest
) z
GROUP BY FLOOR(rank/2),dest
ORDER BY dest, MIN(rank)
An SQLfiddle to test with.
Here is a stored proc,you just put in the delimiter when you call it
DELIMITER $$
DROP PROCEDURE IF EXISTS explode_table $$
CREATE PROCEDURE explode_table(bound VARCHAR(255))
BEGIN
DECLARE id TEXT;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value TEXT;
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c
where a.id=c.id_port and b.id=c.id_dest and dest != '' group by dest;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` VARCHAR(255),
`value` VARCHAR(255) NOT NULL
) ENGINE=Memory;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id, value;
IF done THEN
LEAVE read_loop;
END IF;
SET occurance = (SELECT LENGTH(CONCAT(value,bound))
- LENGTH(REPLACE(CONCAT(value,bound), bound, ''))
+1);
SET i=2;
WHILE i <= occurance DO
SET splitted_value =
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(value,bound),bound,i),bound,-2) ;
INSERT INTO table2 VALUES (id, splitted_value);
SET i = i + 2;
END WHILE;
END LOOP;
SELECT * FROM table2;
CLOSE cur1;
END; $$
CALL explode_table(',')

IF EXISTS with SQL Server 2008

I have a table order in which there are 3 columns
BookCode Quantity Billno
--------------------------
Ed001 2 A1
Ed002 3 A1
Ed003 1 A1
py001 5 A2
ed001 1 A2
ed005 2 A2
ed003 4 A2
Now I want to show my result to user as
table Result
BookCode A1 A2
----------------------
Ed001 2 1
Ed002 3 0
Ed003 1 4
py001 0 5
ed005 0 1
Result is my temporary table, I alter it and add column according to Billno
this works fine
Now I select BookCode and Quantity from table Order and insert it into result
What I want that if there is already a bookcode exist then just update result table otherwise insert a new row with that bookcode.
What should I do for that?
I solved it using Cursor..
create table #Result(BookCode varchar(20),BookRate float)
create table #bill(bilno varchar(max) )
insert into #bill ('A1')
insert into #bill ('A2')
SET #Ordnum = CURSOR FOR
SELECT bilno FROM #bill
OPEN #Ordnum
FETCH NEXT
FROM #Ordnum INTO #billno
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC ('ALTER TABLE #temp ADD [' + #billno+ '] varchar(30)')
SET #getbookcode = CURSOR FOR
SELECT BookCode , BookQuantity , BookRate FROM OrderDetail where BillNo = #billno
OPEN #getbookcode
FETCH NEXT
FROM #getbookcode INTO #Tbookcode,#TbookQty,#TbookRate
WHILE ##FETCH_STATUS = 0
BEGIN
if exists(select BookCode from #Result where BookCode = #Tbookcode and BookRate = #TbookRate)
begin
EXEC('Update #Result set [' + #billno+']= '+#TbookQty+' where BookCode = '''+#Tbookcode+''' and BookRate = '+#TbookRate+'')
end
else
begin
EXEC('Insert Into #Result ([' + #bno + '],BookCode,BookRate) values ( '+#TbookQty +' , '''+#Tbookcode+''','+#TbookRate+')')
end
FETCH NEXT FROM #getbookcode INTO #Tbookcode,#TbookQty , #TbookRate
END
CLOSE #getbookcode
DEALLOCATE #getbookcode
FETCH NEXT FROM #Ordnum INTO #bno
END
CLOSE #Ordnum
DEALLOCATE #Ordnum
select * from #Result
Here #bill is a Temporary table in which Billnum are stored.
Use the MERGE statement. This does an INSERT or UPDATE based on the matching
Something like this, which includes the PIVOT needed
MERGE INTO result R
USING (
SELECT
ISNULL(O1.BookCode, O2.BookCode) AS BookCode,
ISNULL(O1.Quantity, 0) AS A1,
ISNULL(O2.Quantity, 0) AS A2
FROM
(SELECT * FROM #ORDER WHERE BillNo = 'A1') O1
FULL OUTER JOIN
(SELECT * FROM #ORDER WHERE BillNo = 'A2') O2
ON O1.BookCode = O2.BookCode
) src ON R.BookCode = src.BookCode
WHEN MATCHED THEN
UPDATE
SET A1 = src.A1, A2 = src.A2
WHEN NOT MATCHED BY TARGET THEN
INSERT (BookCode, A1, A2)
VALUES (src.BookCode, src.A1, src.A2);

SQL/MYSQL Update child under parent till null

i have a database table like this.
userID RefralID balance
1 0 0
2 1 0
3 2 0
4 3 0
5 8 0
now i want a MYSQL query to update the balance of each child under userID 1.
stuck there from 10 hours, but couldn't find desired Solution.
Results should like this if we update balance=balance + 10 where userID = '1'
as the 2,3,4 are child and grand child of '1' so their balance should be updated
userID RefralID balance
1 0 0
2 1 10
3 2 10
4 3 10
5 8 0
update tblA T2 join
(
SELECT
#r AS _id,
(SELECT #r := userid FROM tblA WHERE refralid = _id limit 1) AS userid,
#l := #l + 1 AS lvl
FROM
(SELECT #r := 1, #l := 0) vars,
tblA m
) T1
ON T1._id = T2.userid
set balance=balance+10
where T2.userid<>1
#r := 1
T2.userid<>1
The value 1 above is the userid=1
Could remove the #l (level) as that is for reference.
http://sqlfiddle.com/#!2/e606a/2
The general approach is :
create table temp1 (int id);
create table temp2 (int id);
insert into temp1 select ReferalID from your_table where userID = 1
while exists (select 1 from temp1)
begin
truncate table temp2
update your_table
set balance = balance + 10
where userID in (select * from temp1)
insert into temp2 select * from temp1
truncate table temp1
insert into temp1
select ReferalID
from your_table
where userID in (select * from temp2)
end

A better solution to replace an old cursor?

I have two tables like this
table1 sorted by paymentid, paydate:
paymentid paydate amount
1 20120101 100
1 20120101 150
1 20120101 150
2 20120115 100
2 20120115 100
...
table2 sorted by paymentid, paydate:
paymentid paydate pay1 pay2 pay3....base on the position from table1...up to pay20
1 20120101 null null null
2 20120115 null null null
...
after update, table2 should be like this
1 20120101 100 100 150 null.....
2 20120115 100 100 null null null.....
The possible solutions i can come up so far are:
1, a temp table for looping thru table1
2, a cursor...current solution:
declare #paymentid int
declare #paydate char(10)
declare #amount money
declare #count int
declare #saveid int
declare #savedate char(10)
delcare cur cursor for select paymentid, paydate, amount from table1 order by paymentid, paydate
open cur
fetch next from cur into #paymentid,#paydate,#amount
while ##fetch_status = 0
if #saveid <> #paymentid or #savedate <> #paydate
set #count = 1
set #saveid = #paymentid
set #savedate = #paydate
else set #count = #count + 1
if #count = 1
update table2
set pay1 = #amount
where paymentid = #paymentid and paydate = #paydate
if #count = 2
update table2
set pay2 = #amount
...
fetch next
...
Any suggestion will be appreciated.
Try this
SQL Fiddle
declare #Test Table (paymentid int,
paydate varchar(25), amount int)
INSERT INTO #Test
SELECT 1,'20120101',100
UNION ALL
SELECT 1,'20120101',150
UNION ALL
SELECT 1,'20120101',150
UNION ALL
SELECT 2,'20120115',100
UNION ALL
SELECT 2,'20120115',100
--INSERT into Table2 //insert here
SELECT * FROM (
SELECT *, 'pay' + CAST(Row_Number() OVER (Partition by paymentid,
paydate order by amount) AS VARCHAR) PayCol
FROM #Test
) V
PIVOT
(
MAX(Amount) For PayCol IN
(pay1,pay2,pay3,pay4, pay5, pay6, pay7, pay8, pay9, pay10,
pay11,pay12,pay13,pay14, pay15, pay16, pay17, pay18, pay19, pay20)
) As P