SQL/MYSQL Update child under parent till null - mysql

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

Related

Update Value in next Row MYSQL

What should i query to update the next ROW2 and so on which will be deducted from previous row.
Update tbl SET BAL= ''
Row 1 which has 10 QTY and 5 BAL
on the next Row 2, get the Previous BAL which is 5 then substract 1 QTY in Row 2, then update the Row 2 BAL = 4 and so on.
My Table
Row
QTY
BAL
1
10
5
2
1
0
3
4
0
DESIRED OUTPUT
Row
QTY
BAL
1
10
5
2
1
4
3
4
0
UPDATE MyTable, ( SELECT `Row`, `QTY`,
#balance := #balance - QTY AS BAL
FROM MyTable
CROSS JOIN ( SELECT #balance := QTY + BAL
FROM MyTable
ORDER BY `Row` LIMIT 1 ) as init
ORDER BY `Row` ASC) data_for_update
SET MyTable.BAL = data_for_update.BAL
WHERE MyTable.`Row` = data_for_update.`Row`;
fiddle
UPDATE MyTable
JOIN ( SELECT t1.`Row`, t1.QTY, t3.init_balance - SUM(t2.QTY) BAL
FROM MyTable t1
JOIN MyTable t2 ON t1.`Row` >= t2.`Row`
CROSS JOIN ( SELECT QTY + BAL init_balance
FROM MyTable
ORDER BY `Row` LIMIT 1 ) t3
GROUP BY t1.`Row`, t1.QTY, t1.BAL, t3.init_balance) data_for_update
ON MyTable.`Row` = data_for_update.`Row`
SET MyTable.BAL = data_for_update.BAL;
fiddle

How to return rows that sum(field) <= value

The concept is to find the rows in which sum(fCurrAmt) may higher than the entered amount but should not lower than entered amount. I dont know how to explain indetail this creteria.
Lets say I have a table demo
Scenario : 1
id fCurrAmt price
------------------
1 1 10
2 1 20
3 2 25
4 3 30
If the entered amount is 3, I need to return first 3 rows
id fCurrAmt price
------------------
1 1 10
2 1 20
3 2 25
In the above scenario, sum(fCurrAmt) is 4 which is higher than entered amount.
Scenario : 2
id fCurrAmt price
------------------
1 1 10
2 1 20
If the entered amount is 3, I need to return there is no records.
In the above scenario, sum(fCurrAmt) is 2 which is lower than entered amount.
I have tried with below code in scenario 1
SELECT a.id,a.price,a.total,a.fCurrAmt from (
select b.id,b.price,b.fCurrAmt,(
select sum(fCurrAmt) from demo c where c.id <= b.id order by c.id
) as total from demo b
) a where a.total <= 3
It returns first 2 records only
Try This You need to use subquery and min with group by. Using subquery we can return the minimum id where the sum is satisfied with given numbers and then join the id to retrieve full rows upto the id
SELECT *
FROM test t
INNER JOIN(
SELECT MIN(id) valId
FROM (
SELECT t.id,
(SELECT SUM(t1.fCurrAmt)
FROM test t1
WHERE t1.id <= t.id) AS Rowsum
FROM test t) t2
WHERE Rowsum >= 3) t1 ON t1.valId >= t.id;
SQL Fiddle http://www.sqlfiddle.com/#!9/a1d07/13
Try this
DECLARE #sumOfFCurrAmt int
DECLARE #sumOfEnteredAmt int
set #sumOfFCurrAmt=(select Sum(fCurrAmt) from demoB)
set #sumOfEnteredAmt=(select sum(fCurrAmt) from demoC)
IF(#sumOfFCurrAmt>#sumOfEnteredAmt)
BEGIN
SELECT top(#sumOfEnteredAmt)* FROM demoB
END
A slightly lengthy way but it works.
First, I would store the sum of fCurrAmt up to the number entered in a temporary table. Hence, the first three statements are DROP, CREATE and INSERT. I would then take that value to check if the sum of those rows until the number entered are greater or lesser, if it is greater, then return all the rows until the threshold else return nothing. Here, sof12 is the same table as scenario 1 and sof14 is the same table as yours in scenario 2.
SCENARIO 1:
DROP TABLE IF EXISTS `tempsum`;
CREATE TABLE tempsum (`sum` integer(13));
INSERT INTO tempsum (SELECT SUM(fCurrAmt) FROM
(SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof12
WHERE #total <= 3) as new2);
SELECT id, fCurrAmt, price FROM (
SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof12
WHERE #total <= 3) As new3 HAVING (SELECT SUM(SUM) FROM tempsum) >= 3;
Output of this case:
id fCurrAmt price
1 1 10
2 1 20
3 2 25
SCENARIO 2:
DROP TABLE IF EXISTS `tempsum`;
CREATE TABLE tempsum (`sum` integer(13));
INSERT INTO tempsum (SELECT SUM(fCurrAmt) FROM
(SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof14
WHERE #total <= 3) as new2);
SELECT id, fCurrAmt, price FROM (
SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof14
WHERE #total <= 3) As new3 HAVING (SELECT SUM(SUM) FROM tempsum) >= 3;
Output of this case: No records returned.

Insert a new column based on existing column values

I have a table table1 in mysql like this:
price item count
100 xyz 5
200 xyz 1
300 xyz 4
400 abc 1
500 abc 2
I want to insert a new column 'new_price' that will hold the 'price' for that 'item' with the highest 'count'. So the new table will be
price item count new_price
100 xyz 5 100
200 xyz 1 100
300 xyz 4 100
400 abc 1 500
500 abc 2 500
What is the most efficient way of doing this? Thanks very much for your help.
I think the easiest approach is to use variables:
select t.*,
(#price := if(#i = item, #price,
if(#i := item, price, price)
)
) as new_price
from table1 t cross join
(select #i := '', #price := -1) vars
order by item, count desc;
If you actually want to update values in the table, you can fit this into an update as well:
update table1 t join
(select t.*,
(#price := if(#i = item, #price,
if(#i := item, price, price)
)
) as new_price
from table1 t cross join
(select #i := '', #price := -1) vars
order by item, count desc
) tp
on tp.item = t.item and tp.price = t.price and tp.count = t.count
set t.new_price = tp.price;

mysql table with duplicate records

I have a table
email(email varchar(30),id integer(10),duplicated varchar(10))
with records
sai#gmail.com 101 null
kiran#gmail.com 102 null
sai123#gmail.com 103 null
sai#gmail.com 101 null
kiran#gmail.com 102 null
Now my question is i need to get "yes" in the duplicated column for the two duplicated records for the second time. so, the output table should be
sai#gmail.com 101 null
kiran#gmail.com 102 null
sai123#gmail.com 103 null
sai#gmail.com 101 yes
kiran#gmail.com 102 yes
Try this
update email set duplicated =
(case when (select count(*) from email x where x.email = e.email) > 1 then "yes" else null)
edited: this will update table
You can try this query for viewing:
select numerated.email, numerated.id, (case when cnt=1 OR numerated.rnum=grouped.min_rnum then null else "yes" end) as duplicated
from
(select #i := #i + 1 as rnum, email.* from email, (select #i:=0) as c order by id) as numerated
left join
(select email, id, min(rnum) as min_rnum, count(rnum) as cnt
from (select #i := #i + 1 as rnum, email.* from email, (select #i:=0) as c order by id) as numerated
group by email, id
) as grouped
on numerated.email=grouped.email and numerated.id=grouped.id
order by id;
Could you explain your situation in details? It looks like it needs another solution, not just SELECT query.
And try this one for updating:
update email u, (select #i:=0) urnum
set
id = id + (#i:=#i + 1) - #i,
duplicated = (
select duplicated from (
select
numerated.email,
numerated.id,
(case when cnt=1 OR numerated.rnum=grouped.min_rnum then null else "yes" end) as duplicated,
rnum
from
(select #i := #i + 1 as rnum, email.* from email, (select #i:=0) as c ) as numerated
left join
(select email, id, min(rnum) as min_rnum, count(rnum) as cnt
from (select #i := #i + 1 as rnum, email.* from email, (select #i:=0) as c ) as numerated
group by email, id
) as grouped
on numerated.email=grouped.email and numerated.id=grouped.id
order by rnum
) found_duplicates
where u.email=found_duplicates.email and u.id=found_duplicates.id and #i=found_duplicates.rnum
limit 1
)
;
It looks like it works, but you shouldn't rely on it.
If it is possible, you should do any of this:
1. change table structure - add unique field
2. change table filling logic - check uniqueness before inserting new row and insert it with proper 'duplicates' field value;
3. repopulate via temporary table like this:
CREATE TEMPORARY TABLE tmp_email AS <... 'SELECT' version of my query ...>;
TRUNCATE TABLE email;
INSERT INTO email SELECT * FROM tmp_email;

SQL SERVER generating row number based on criteria

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