fetching a records from binary tree in mysql - mysql

I need to know that how to fetch a record from table which consist of certain records varies with different id named[sponser_id]
from the above img i give you a simple scenario..say for ex: the rounded person is said to be the person A.
when Person A loggin into his/her acc. It should show how many members(count) are coming under his/her control.
Mysql table has columns like
sponser_id refers to the parent user_id.
sponser_id varies with parent_user who referred them.
Here my question is, How to retrieve the count of members under this particular person..
I have only user_id & sponser_id columns alone in my table.

Here is an MLM tree I once implemented here, I think I deleted the answer cuz it was not appreciated :)
I always do these with Stored Procedures. A member can have a parent, like your setup.
The output showed downline sales and a commission at 5%
Schema
-- drop table member;
create table member
( memberId int not null auto_increment primary key,
handle varchar(255) not null,
parentId int null,
key (parentId)
);
-- drop table sales
create table sales
( -- sales of Products abbreviated
id int auto_increment primary key,
memberId int not null,
amount decimal(10,2) not null,
saleDate datetime not null,
CONSTRAINT `fk_member`
FOREIGN KEY (memberId)
REFERENCES member(memberId)
);
insert member(handle,parentId) values ('Johnny Two-Thumbs',null); -- 1
insert member(handle,parentId) values ('Jake the Enforcer',null); -- 2
insert member(handle,parentId) values ('Innocent Kim',2); -- 3
insert member(handle,parentId) values ('Karen',2); -- 4
insert member(handle,parentId) values ('Henry',2); -- 5
insert member(handle,parentId) values ('Shy Sales-less Sam',5); -- 6
insert member(handle,parentId) values ('Pete',5); -- 7
insert member(handle,parentId) values ('Iowa Mom',7); -- 8
insert member(handle,parentId) values ('Albuquerque Agoraphobiac',7); -- 9
insert sales (memberId,amount,saleDate) values (2,1,'2015-01-01');
insert sales (memberId,amount,saleDate) values (5,10,'2015-01-20');
insert sales (memberId,amount,saleDate) values (5,15.50,'2015-01-22');
insert sales (memberId,amount,saleDate) values (7,101.12,'2015-02-01');
insert sales (memberId,amount,saleDate) values (7,201.12,'2015-03-01');
insert sales (memberId,amount,saleDate) values (7,109,'2015-04-01');
insert sales (memberId,amount,saleDate) values (7,45,'2015-05-01');
insert sales (memberId,amount,saleDate) values (8,111,'2015-04-20');
insert sales (memberId,amount,saleDate) values (8,99.99,'2015-05-22');
insert sales (memberId,amount,saleDate) values (9,0.04,'2015-06-20');
insert sales (memberId,amount,saleDate) values (9,1.23,'2015-06-24');
Stored Procedure
drop procedure if exists showAllDownlineSales;
DELIMITER $$
create procedure showAllDownlineSales
(
theId int
)
BEGIN
-- theId parameter means i am anywhere in hierarchy of membership
-- and i want all downline sales
-- return 1 row: sales amt for downlines, and that amt * 5%, and total children (including children-of-children)
declare bDoneYet boolean default false;
declare working_on int;
declare theCount int;
declare downlineSales decimal(10,2);
declare commish decimal(10,2);
CREATE temporary TABLE xxFindSalesxx
(
memberId int not null,
processed int not null, -- 0 for not processed, 1 for processed
salesTotal decimal(10,2) not null
);
set bDoneYet=false;
insert into xxFindSalesxx (memberId,processed,salesTotal) select theId,0,0;
while (!bDoneYet) do
select count(*) into theCount from xxFindSalesxx where processed=0;
if (theCount=0) then
-- found em all
set bDoneYet=true;
else
-- one not processed yet, insert its children for processing
SELECT memberId INTO working_on FROM xxFindSalesxx where processed=0 limit 1;
insert into xxFindSalesxx (memberId,processed,salesTotal)
select memberId,0,0 from member
where parentId=working_on;
-- update xxFindSalesxx
-- join sales
-- on sales.memberId=xxFindSalesxx.memberId
-- set salesTotal=sum(sales.amount)
-- where xxFindSalesxx.memberId=working_on;
update xxFindSalesxx
set salesTotal=(select ifnull(sum(sales.amount),0) from sales where memberId=working_on)
where xxFindSalesxx.memberId=working_on;
-- mark the one we "processed for children" as processed
update xxFindSalesxx set processed=1 where memberId=working_on;
end if;
end while;
delete from xxFindSalesxx where memberId=theId;
select sum(salesTotal),count(*) into downlineSales,theCount from xxFindSalesxx;
drop table xxFindSalesxx;
select downlineSales,round(downlineSales*0.05,2) as commission,theCount; -- there is your answer, 1 row
END
$$
DELIMITER ;
Test it
call showAllDownlineSales(2); -- 693.00 34.69 7
call showAllDownlineSales(1); -- null null 0
call showAllDownlineSales(5); -- 668.50 33.43 4

Related

After Creating after update trigger i cant update any

DROP TABLE IF EXISTS Sales;
CREATE TABLE Sales (
id INT AUTO_INCREMENT,
product VARCHAR(100) NOT NULL,
quantity INT NOT NULL DEFAULT 0,
fiscalYear SMALLINT NOT NULL,
fiscalMonth TINYINT NOT NULL,
CHECK(fiscalMonth >= 1 AND fiscalMonth <= 12),
CHECK(fiscalYear BETWEEN 2000 and 2050),
CHECK (quantity >=0),
UNIQUE(product, fiscalYear, fiscalMonth),
PRIMARY KEY(id)
);
DROP TABLE IF EXISTS log;
CREATE TABLE log (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
text VARCHAR(100)
);
Triggers
CREATE DEFINER=`root`#`localhost` TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON `sales`
FOR EACH ROW
BEGIN
INSERT INTO log VALUES(NOW(),CONCAT('Update Student Record ', OLD.quantity));
END
UPDATE test for.sales SET quantity = 36
WHERE (id = 1);
ERROR 1136: 1136: Column count doesn't match value count at row 1
Iam new in mySQL Please help
You should specify columns in INSERT statement in your trigger explicitly, as you do not set all values in a row (auto incremented column excluded).
So it would be
INSERT INTO log(timestamp, text) VALUES (NOW(),CONCAT('Update Student Record ', OLD.quantity));
You have some errors.
First based on your trigger you need another column on log table which is as following
quantity INT NOT NULL DEFAULT 0
Second , do not use Keywords and Reserved Words like text and timestamp, it is a bad practice. If you do please put it inside backticks
Third your insert statement should be
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
there is no need for CONCAT.
Fourth,
`sales`
table is not the same as Sales table, because you have used backticks.
Full working trigger below:
DELIMITER //
CREATE TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON Sales
FOR EACH ROW
BEGIN
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
END//
DELIMITER ;
Check working demo:
https://www.db-fiddle.com/f/iqwShcHK3AGJvU4MDbxDku/0

Update Table from Trigger

I have the following tables in a database:
CREATE TABLE `CRUISE-RES` (
`cruiseid` INT,
`end-day` DATE,
`start-day` DATE
PRIMARY KEY (`cruiseid`));
CREATE TABLE `ROOM` (
`cruise-id` INT,
`price` FLOAT,
FOREIGN KEY (`cruise-id`));
CREATE TABLE `PROFIT` (
`cruiseid` INT,
`total` FLOAT);
With the following sample table inserts:
-- cruise table inserts
insert into `CRUISE-ID` (`cruiseid`,`start-day`,`end-day`)
values (1, '2022/01/01', '2022/01/05'), (1, '2022/01/05', '2022/01/10'), (2, '2022/01/05', '2022/01/10')
-- room table inserts
insert into ROOM (price,`cruise-id`)
values (5,1), (10,1), (25,2)
I also have the following function that shows the profit of each cruiseid based on the number of days in the CRUISE-RES * price per day.
SELECT c.`cruiseid`, sum(rm.`price`*(DATEDIFF(c.`end-date`, c.`start-date`))) AS 'total_profit'
FROM ROOM rm
JOIN `CRUISE-RES` c
ON rm.`cruise-id` = c.cruiseid
GROUP BY rm.`cruise-id`,'cruiseid'
How can I use this information on a trigger that updates the PROFIT table after each insert into CRUISE-RES table?

how to have insertion happen only once in a cursor loop in mysql

This procedure takes a brand name and matches it with the brandnames in the product table and creates a campaign.
When I call this procedure BrandNameCampaign with an input of a brandname that does NOT exist in the product table it is still creating a campaign. I know it's because I have kept the insertion query outside of the loop where it is checking whether camp_c cursor has null values or not.
However, if I put the query inside the repeat loop of the cursor, it generates an error code: 1062 duplicate entry 'x' (x being an int) for key 'campaign.PRIMARY'.
How do I fix my code so that a new campaign does not get inserted into the table without creating/firing a trigger. I want it to work within this procedure.
Table code
create table Product (
ProductID int not null,
ProductType varchar (20) not null,
PackageType varchar(20) not null,
YearProduced int not null,
Price float not null,
Brand varchar(255) not null,
PRIMARY KEY (ProductID)
)
create table Campaign (
CampaignID int not null,
CampaignStartDate date not null,
CampaignEndDate date,
PRIMARY KEY (CampaignID)
)
create table DiscountDetails (
ProductID int not null,
CampaignID int not null,
MembershipLevel varchar(20) not null,
Discount int not null,
primary key (ProductID, CampaignID, MembershipLevel),
foreign key (ProductID) references Product (ProductID),
foreign key (CampaignID) references Campaign (CampaignID)
)
Procedure code
create procedure BrandNameCampaign (in brandname varchar(50))
begin
declare v_finished int default 0;
declare prod_id int;
declare newcampid int;
declare camp_brand varchar(255);
declare camp_c cursor for
select productid, brand
from product
where brandname = brand
order by price desc limit 5;
declare continue handler for not found set v_finished = 1;
SELECT
MAX(CampaignID)
INTO newcampid FROM
campaign;
set newcampid = 1 + newcampid;
insert into `Campaign`(`CampaignID`,`CampaignStartDate`,`CampaignEndDate`) values
(newcampid,date_add(curdate(), interval 4 week), date_add( curdate(), interval 8 week));
-- working with cursor
open camp_c;
repeat
fetch camp_c into prod_id, camp_brand;
if not (v_finished = 1) then
insert into discountdetails values (prod_id, newcampid, 'S', 20);
insert into discountdetails values (prod_id, newcampid, 'G', 30);
insert into discountdetails values (prod_id, newcampid, 'P', 40);
end if;
until v_finished
end repeat;
close camp_c;
end//
One solution would be to use:
insert ignore into `Campaign` ...
The ignore means do not generate an error if the insert results in a duplicate key or other type of error.
A different solution might be to use a boolean variable:
declare v_do_insert tinyint(1) default true;
...
repeat
insert into `Campaign`(`CampaignID`,`CampaignStartDate`,`CampaignEndDate`)
select newcampid, date_add(curdate(), interval 4 week), date_add( curdate(), interval 8 week)
from dual where v_do_insert=true;
set v_do_insert = false;
...
until v_finished
end repeat;
In this example, the insert...select would insert one row the first time through the loop, but then on subsequent iterations of the loop, v_do_insert is now false, so the insert...select would insert zero rows.

SQL max number of records

So I have the following table:
CREATE TABLE Hospital_MedicalRecord(
recNo CHAR(5),
patient CHAR(9),
doctor CHAR(9),
enteredOn DATE NOT NULL,
diagnosis VARCHAR(50) NOT NULL,
treatment VARCHAR(50),
PRIMARY KEY (recNo, patient),
FOREIGN KEY (patient) REFERENCES Hospital_Patient(NINumber),
FOREIGN KEY (doctor) REFERENCES Hospital_Doctor(NINumber)
);
I want to make it so there are never more that 65,535 medical records for a single patient. Am I supposed to make a new statement or should I implement it in the table above. I can post the patient table if needed.
You would typically use a before insert trigger for this, that raises an error if the number of records for a patient reached the limit and a new insert is attempted:
delimiter //
create trigger Trg_Hospital_MedicalRecord
before insert on Hospital_MedicalRecord
for each row
begin
if (
select count(*) from Hospital_MedicalRecord where patient = new.patient
) = 65535 then
set msg = concat('Patient ', new.patient, ' cannot have more than 65535 records');
signal state '45000' set message_text = msg;
end if;
end
//
delimiter ;
I would assume that you should not allow a patient to be updated on an existing record. But if this may happen, then you also need a before update trigger (with the very same code).
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(id SERIAL PRIMARY KEY, user_id INT NOT NULL);
INSERT INTO my_table (user_id)
SELECT 1
FROM (SELECT 1) x
LEFT
JOIN (SELECT user_id FROM my_table GROUP BY user_id HAVING COUNT(*) >=3) y
ON y.user_id = 1
WHERE y.user_id IS NULL
LIMIT 1;
This limits INSERTS to 3 per user_id.

stored procedure + mysql question

I have created a stored procedure to delete data from multiple tables. my work flow as follows
I'm using mysql 5.0 and running on linux
table dependencies as follows
table C depending on table B
table B depending on table A
I want to delete a record in table A and delete all the related records in tables B and C
1 - delete all the data from detail tables (C) (with stored procedure sp_delete_from_C)
2 - delete related data immediate child table (B) (with stored procedure sp_delete_from_B)
3 - delete master table (A) (with stored procedure sp_delete_from_A)
I have wrote the following procedure
CREATE PROCEDURE sp_A_rollback(IN aId INT UNSIGNED)
READS SQL DATA
BEGIN
DECLARE b_id INT DEFAULT 0;
DECLARE cur_1 CURSOR FOR SELECT id FROM b where a_id=aId;
OPEN cur_1;
read_loop: LOOP
FETCH cur_1 INTO a_id;
CALL sp_delete_from_C(b_id);
END LOOP;
CLOSE cur_1;
CALL sp_delete_from_B(aId);
CALL sp_delete_from_A(aId);
END //
My question is,
If i run these procedures individually it works
but if u run sp_A_rollback it execute only 'sp_delete_from_C'
I dont have any idea why its not calling the other 2 sps. I'm a newbee to mysql stored procedures. please can someone help me
thanks in advance
sameera
I have no idea why you're using cursors - all you need is something like the following:
drop procedure if exists cascade_delete_tableA;
delimiter #
create procedure cascade_delete_tableA
(
in p_id int unsigned
)
begin
delete from tableC where a_id = p_id;
delete from tableB where a_id = p_id;
delete from tableA where id = p_id;
end#
delimiter ;
call the stored procedure from your application code wrapped up in a transaction.
EDIT
You'll need to use a join to delete rows from your tableC. Here's a more comprehensive example for you to study http://pastie.org/1435521. Also, your cursor loop isnt fetching into the correct variable which is why it's not working in it's current form. I would still recommend you examine the following...
-- TABLES
drop table if exists customers;
create table customers
(
cust_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null
)
engine=innodb;
drop table if exists orders;
create table orders
(
order_id int unsigned not null auto_increment primary key,
cust_id smallint unsigned not null
)
engine=innodb;
drop table if exists order_items;
create table order_items
(
order_id int unsigned not null,
prod_id smallint unsigned not null,
primary key (order_id, prod_id)
)
engine=innodb;
-- STORED PROCS
drop procedure if exists cascade_delete_customer;
delimiter #
create procedure cascade_delete_customer
(
in p_cust_id smallint unsigned
)
begin
declare rows int unsigned default 0;
-- delete order items
delete oi from order_items oi
inner join orders o on o.order_id = oi.order_id and o.cust_id = p_cust_id;
set rows = row_count();
-- delete orders
delete from orders where cust_id = p_cust_id;
set rows = rows + row_count();
-- delete customer
delete from customers where cust_id = p_cust_id;
select rows + row_count() as rows;
end#
delimiter ;
-- TEST DATA
insert into customers (name) values ('c1'),('c2'),('c3'),('c4');
insert into orders (cust_id) values (1),(2),(3),(1),(1),(3),(2),(4);
insert into order_items (order_id, prod_id) values
(1,1),(1,2),(1,3),
(2,5),
(3,2),(3,5),(3,8),
(4,1),(4,4),
(5,2),(5,7),
(6,4),(6,8),(6,9),
(7,5),
(8,3),(8,4),(8,5),(8,6);
-- TESTING
/*
select * from customers where cust_id = 1;
select * from orders where cust_id = 1;
select * from order_items oi
inner join orders o on oi.order_id = o.order_id and o.cust_id = 1;
call cascade_delete_customer(1);
*/