sql trigger two tables not empty - mysql

CREATE TRIGGER bookAvailDelete after delete on Auction
for each row
Begin
if (Select count(OLD.isbn) from Auction a > 0)
update Book b
set available =false
where OLD.isbn = isbn
;
I have two tables, Auction and Book. Auction has isbn key that references Book isbn primary key. Many auctions can point to the same book so I want this trigger to check on every auction delete, to see if it was the last auction of that book isbn. If it is, then it sets the "available" key in Book to false.

Typically, unless there were performance reasons, you'd just calculate this on the fly (instead of storing it) with queries like:
SELECT b.*
, EXISTS(SELECT * FROM Auction a WHERE a.isbn = b.isbn) AS available
FROM Book b
or (my preferred format):
SELECT b.*, (a.isbn IS NOT NULL) AS available
FROM Book b
LEFT JOIN (SELECT DISTINCT isbn FROM Auction) AS a ON b.isbn = a.isbn
;

Assuming available is int:
drop trigger bookAvailDelete;
DELIMITER //
CREATE TRIGGER bookAvailDelete after delete on Auction
for each row begin
if NOT EXISTS(Select * from Auction where isbn = old.isbn)
then
update Book b set b.available = 0 where b.isbn = old.isbn;
end if;
end;

Please compile and test below. Use a variable for the count and put end if at the end too.
delimiter //
CREATE TRIGGER bookAvailDelete after delete on Auction
for each row
Begin
DECLARE updatecount INT;
set updatecount = ( Select count(*) from Auction a where a.isbn = OLD.isbn);
if updatecount > 0 then
update Book b
set b.available =false
where b.isbn = OLD.isbn;
end if;
End;//

Related

mySQL finding available book and loaning it

I have created a library system and i'm trying to set up a procedure which allows you to enter an isbn and student number and from that it checks the database to see if the book is available if it is, book it. If not then give an error message.
My tables are as follows:
book - pk_isbn, author, title
copy - pk_code, duration, fk_isbn
loan - fk_code, fk_no, pk_taken, due, return_date
student - pk_no, name, school, allowedtobook
Here is what i currently have code wise *some is just sorta pseudo code i guess as it doesn't work at all currently.
DELIMITER $$
CREATE PROCEDURE issue_loan (IN book_isbn CHAR(17), IN stu_no INT)
BEGIN
DECLARE availablebooks INT;
DECLARE allowedtobook BIT;
SET availablebooks = (SELECT DISTINCT
c.isbn
FROM
loan l
INNER JOIN
student s ON l.no = s.no
INNER JOIN
copy c ON l.code = c.code
WHERE
return_date IS NOT NULL);
IF(availablebooks = book_isbn) AND s.embargo = 0 THEN
INSERT INTO loan VALUES
(code, stu_no, CURDATE(), CURDATE()+c.duration,'');
ELSE
SIGNAL SQLSTATE '450000' SET MESSAGE_TEXT ='no Books Available';
END IF;
END$$
The part which return_date IS NOT NULL should be correct as it finds any books which haven't been returned yet. Meaning they're currently booked out.
You can't set a variable to a query result set, a variable can only hold one value. You should simply add the comparison with book_isbn and s.embargo = 0 to the query.
SET #book_available = (SELECT COUNT(*)
FROM loan l
INNER JOIN student s ON l.no = s.no
INNER JOIN copy c ON l.code = c.code
WHERE c.fk_isbn = book_isbn AND s.embargo = 0 AND l.return_date IS NULL);
IF #book_available
THEN
INSERT INTO loan (fk_code, fk_no, pk_taken, due, return_date)
VALUES (code, stu_no, CURDATE(), CURDATE()+c.duration,'');
ELSE SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT ='no Books Available';
END IF

change one table data when single column all data of another table is equal to some value in mysql

I have created 2 tables named snag_list and defect_list. I need to change the status field of snag_list to 2 when all the defect_list status should to be 2
Not sure if this helps but try to create a trigger for defect_list
and check the distinct count of status column if it is one and the value is 2 then update the snag_list a example would look like this
DELIMITER $$
CREATE TRIGGER checkstatus
AFTER UPDATE ON defect_list
FOR EACH ROW
BEGIN
DECLARE cnt INT
SELECT COUNT(DISTINCT status) FROM defect_list INTO cnt
DECLARE st INT
SELECT DISTINCT status FROM defect_list LIMIT 1 INTO st
IF(cnt = 1 AND st = 2)
UPDATE snag_list SET status = 2
ENF IF
END$$
DELIMITER ;
Your question is very vague but I guess this is what you may be looking for.
DECLARE
count_rec VARCHAR2(10);
data_rec VARCHAR2(10);
BEGIN
SELECT COUNT(DISTINCT status) INTO count_rec FROM defect_list;
SELECT DISTINCT status INTO data_rec FROM defect_list;
IF (count_rec = '1' AND data_rec = '2') THEN
UPDATE snag_list SET status = '2';
END IF;
END;
edit -> You can change the datatype of the 2 variables as required. Go with VARCHAR2 if you're unsure whether the data would be numeric.

Create a trigger to insert a row in order table on payment status is complete

I want to create a trigger so as payment process regarding any deal is completed and admin set status to completed a new entry inserted in to order table with user detail like name and address and deal detail.
begin
declare receipent_name varchar(225);
declare receipent_number varchar(225);
declare receipent_address varchar(225);
IF (new.dj_status == 'completed')
THEN
select `user_name` into receipent_name,
`user_contact_number` into receipent_number
`user_address_google` into receipent_address
from users JOIN user_addresses ON user_id = user_address_user_id WHERE user_id = old.dj_user_id;
INSERT INTO `order`(`or_dj_id`, `or_track_number`, `or_recipient_name`, `or_recipient_number`, `or_address`)
VALUES (old.dj_id,'',receipent_name,receipent_number,receipent_address);
END IF;
end

Update a second table, using trigger

I'm trying to create a simple trigger, but, i can't set the media value on prato if the value comes from #total or #sum.
I've already tested to substitute them for "1" or "1+1", to see if i wasn't even updating correctly or the problem was the operation itself.
DELIMITER $$
CREATE
TRIGGER ratings_prato BEFORE INSERT ON ratings
FOR EACH ROW BEGIN
SET #total = #total + 1;
SET #sum = NEW.stars + #sum;
UPDATE prato p SET p.media = #sum/#total WHERE p.id = 1;
END;
$$
DELIMITER ;
Any ideias?
I've completed mislead the FOR EACH ROW on trigger. I thought, every time it was updated, It would run trough all rows. But, instead of it, it executes for each row it was updated, which, make things possible.
This was the last result of my trigger, inserting on ratings, and updating on food(which was prato)
DELIMITER $$
CREATE
TRIGGER inserting_dishes_rating AFTER INSERT ON ratings
FOR EACH ROW BEGIN
IF(NEW.food_id is not null) THEN
SET #media = (SELECT SUM(stars) FROM ratings WHERE food_id = NEW.food_id) / (SELECT COUNT(*) FROM ratings WHERE food_id = NEW.food_id);
      UPDATE foods f SET f.rate = #media WHERE f.id = NEW.food_id;
    ELSE
  SET #media = (SELECT SUM(stars) FROM ratings WHERE restaurant_id = NEW.restaurant_id) / (SELECT COUNT(*) FROM ratings WHERE restaurant_id = NEW.restaurant_id);  
UPDATE restaurants r SET r.rate = #media WHERE r.id = NEW.restaurant_id;
END IF;
END

MySQL Count products from all subcategories

I have two tables; categories and products. For each category i would like to count how many products there are in all of its subcategories. I already have counted how many are in each category. Example tables are:
Categories:
ID ParentID ProductCount SubCategoryProducts
1 NULL 0
2 1 2
3 2 1
Products:
ProductID CategoryID
123 2
124 2
125 3
So i would like my function to make:
ID ParentID ProductCount SubCategoryProducts
1 NULL 0 3
2 1 2 1
3 2 1 0
It simply needs to be as a select query, no need to update the database.
Any ideas?
EDIT: SQL FIddle: http://sqlfiddle.com/#!2/1941a/4/0
If it were me I'd create a STORED PROCEDURE. The other option is to loop with PHP through the first query, then for each ID run another query - but this kind of logic can slow down your page drastically.
Here's a nice tutorial on stored procedures: http://net.tutsplus.com/tutorials/an-introduction-to-stored-procedures/
Basically you run the same loops I mentioned above you would with PHP (but it runs much faster). The procedure is stored in the database and can be called like a function. The result is the same as a query.
As requested, here's a sample procedure (or rather, it uses two) in my instance, "ags_orgs" acts in a similar way to your categories where there is a parentOrgID. "getChildOrgs" also acts kind of like a redundant function since I had no idea how many levels down I had to go (this was written for MSSQL - there are probably differences with mySQL) Unfortunately this doesn't count rows, rather it gets data. I highly recommend following a tutorial or two to get a better grip on how it works:
USE [dbname]
GO
/****** Object: StoredProcedure [dbo].[getChildOrgs] Script Date: 09/26/2012 15:30:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[getChildOrgs]
#myParentID int,
#isActive tinyint = NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE #orgID int, #orgName varchar(255), #level int
DECLARE cur CURSOR LOCAL FOR SELECT orgID FROM dbo.ags_orgs WHERE parentOrgID = #myParentID AND isActive = ISNULL(#isActive, isActive) ORDER BY orderNum, orgName
OPEN cur
fetch next from cur into #orgID
WHILE ##fetch_status = 0
BEGIN
INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,##NESTLEVEL-1 AS level FROM dbo.ags_orgs WHERE orgID = #orgID
EXEC getChildOrgs #orgID, #isActive
-- get next result
fetch next from cur into #orgID
END
CLOSE cur
DEALLOCATE cur
END
GO
Which is called by this proc:
USE [dbname]
GO
/****** Object: StoredProcedure [dbo].[execGetChildOrgs] Script Date: 09/26/2012 15:29:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[execGetChildOrgs]
#parentID int,
#isActive tinyint = NULL,
#showParent tinyint = NULL
AS
BEGIN
CREATE TABLE #temp_childOrgs
(
orgID int,
orgName varchar(255),
description text,
parentOrgID int,
adminID int,
isActive tinyint,
level int
)
-- if this isn't AGS top level (0), make the first record reflect the requested organization
IF #parentID != 0 AND #showParent = 1
BEGIN
INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,0 AS level FROM dbo.ags_orgs WHERE orgID = #parentID
END
exec getChildOrgs #parentID, #isActive
SELECT * FROM #temp_childOrgs
DROP TABLE #temp_childOrgs
END
GO
Here is my procedure for counting products in all subcategories
DELIMITER $$
CREATE PROCEDURE CountItemsInCategories(IN tmpTable INT, IN parentId INT, IN updateId INT)
BEGIN
DECLARE itemId INT DEFAULT NULL;
DECLARE countItems INT DEFAULT NULL;
DECLARE done INT DEFAULT FALSE;
DECLARE recCount INT DEFAULT NULL;
DECLARE
bufItemCategory CURSOR FOR
SELECT
itemCategory.id AS id,
COUNT(CASE WHEN item.isVisible = 1 then 1 ELSE NULL END) items
FROM
itemCategory
LEFT JOIN item ON
item.categoryId = itemCategory.id
WHERE
itemCategory.isVisible = 1 AND itemCategory.categoryParentId = parentId
GROUP BY
itemCategory.id
ORDER BY
itemCategory.name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET max_sp_recursion_depth = 10000;
IF tmpTable = 1 THEN
DROP TEMPORARY TABLE IF EXISTS tblResults;
CREATE TEMPORARY TABLE IF NOT EXISTS tblResults(
id INT NOT NULL PRIMARY KEY,
items INT
);
END IF;
OPEN bufItemCategory;
Reading_bufItemCategory: LOOP
FETCH FROM bufItemCategory INTO itemId, countItems;
IF done THEN
LEAVE Reading_bufItemCategory;
END IF;
IF tmpTable = 1 THEN
INSERT INTO tblResults VALUES(itemId, countItems);
ELSE
UPDATE tblResults SET items = items + countItems WHERE id = updateId;
END IF;
SET recCount = (SELECT count(*) FROM itemCategory WHERE itemCategory.categoryParentId = itemId AND itemCategory.isVisible = 1);
IF recCount > 0 THEN
CALL CountItemsInCategories(0, itemId, CASE WHEN updateId = 0 then itemId ELSE updateId END);
END IF;
END LOOP Reading_bufItemCategory;
CLOSE bufItemCategory;
IF tmpTable = 1 THEN
SELECT * FROM tblResults WHERE items > 0;
DROP TEMPORARY TABLE IF EXISTS tblResults;
END IF;
END $$
DELIMITER;
To call procedure just run:
CountItemsInCategories(firstLoop,parentId,updateId);
Where parameters are:
firstLoop - always "1" for first loop
parentId - parent of subcategories
updateId - id of row to update, always "0" for first loop
On example:
CountItemsInCategories(1,1,0);
I hope this example will be useful to someone.
This assumes you have
Product table named prods
prod_id|categ_id
and Category table named categ
categ_id|parent_categ_id
As you seem to be using Adjacency List structure where foreign key parent_categ_id column references prod_id column at the same table
the following query should work
select c1.categ_id,c1.parent_categ_id,count(prods.prod_id)
as product_count from categ c1
join prods on prods.categ_id=c1.categ_id or prods.categ_id
in( with recursive tree(id,parent_id)as
(select categ_id,parent_categ_id from categ
where categ_id=c1.categ_id
union all
select cat.categ_id,cat.parent_categ_id from categ cat
join tree on tree.id=cat.parent_categ_id) select id from tree)
group by c1.categ_id,c1.parent_categ_id
order by product_count
You can do this in one statement if you have a limit on the depth of the hierarchy. You said you only have 4 levels in total.
SELECT SUM(ProductCount)
FROM (
SELECT c0.ID, c0.ProductCount
FROM Categories AS c0
WHERE c0.ID = 1
UNION ALL
SELECT c1.ID, c1.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
WHERE c0.ID = 1
UNION ALL
SELECT c2.ID, c2.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
JOIN Categories AS c2 ON c1.ID = c2.ParentID
WHERE c0.ID = 1
UNION ALL
SELECT c3.ID, c3.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
JOIN Categories AS c2 ON c1.ID = c2.ParentID
JOIN Categories AS c3 ON c2.ID = c3.ParentID
WHERE c0.ID = 1
) AS _hier;
That'll work for this query if you store the hierarchy in the way you're doing, which is called Adjacency List. Basically, the ParentID is the way each node records its position in the hierarchy.
There are a few other ways of storing hierarchies that allow for easier querying of whole trees or subtrees. The best data organization depends on which queries you want to run.
Here are some more resources:
Models for Hierarchical Data with SQL and PHP (user # RaymondNijland linked to it in a comment)
I gave that presentation as a webinar (free to view the recording, but requires registration).
My book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
What is the most efficient/elegant way to parse a flat table into a tree?