Error creating stored procedure - mysql

MariaDB 10 on Windows
When creating the following stored procedure in MySQL Workbench, I get error 1064. The error is marked on the last line "LIMIT 1". Trying to create it in HeidiSQL also returns error 1064. This suggests a missing parentheses, but none is missing.
Any help appreciated:
CREATE PROCEDURE `available_room` (in p_client_id int(11), in p_room_id int(11), in dateQF date, in a_gender varchar(3))
BEGIN
SELECT
room_name,
room_type_name,
counted,
num_guests,
minimum,
room_id AS room_id_selected
FROM (SELECT * FROM room
WHERE room_id=p_room_id AND
(NOT EXISTS (
SELECT * FROM invoice
WHERE client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
commit_invoice=1 AND
room_id = product_id AND
gender != a_gender
) OR
(NOT EXISTS (
SELECT * FROM invoice
WHERE client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
commit_invoice=1 AND
room_id = product_id
))))A
LEFT JOIN room_type_content ON
room_type_content.room_type_id=A.room_type AND
language_id='en'
LEFT JOIN(SELECT product_id, count(product_id)AS counted, MIN(NULLIF(quantity,0))AS minimum FROM invoice
WHERE gender =a_gender AND
client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
(commit_invoice=1 OR commit_invoice=3) AND
invoice_set !='SYSTEM'
GROUP BY product_id)B
ON B.product_id=p_room_id
ORDER BY counted DESC
LIMIT 1
END

You forgot a semi-colon after LIMIT 1.
You also need to change the standard delimiter before and after creating the procedure.
Documentation: CREATE PROCEDURE and CREATE FUNCTION Syntax
Fixed version of your code:
DELIMITER $$
CREATE PROCEDURE `available_room` (in p_client_id int(11), in p_room_id int(11), in dateQF date, in a_gender varchar(3))
BEGIN
SELECT
room_name,
room_type_name,
counted,
num_guests,
minimum,
room_id AS room_id_selected
FROM (SELECT * FROM room
WHERE room_id=p_room_id AND
(NOT EXISTS (
SELECT * FROM invoice
WHERE client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
commit_invoice=1 AND
room_id = product_id AND
gender != a_gender
) OR
(NOT EXISTS (
SELECT * FROM invoice
WHERE client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
commit_invoice=1 AND
room_id = product_id
))))A
LEFT JOIN room_type_content ON
room_type_content.room_type_id=A.room_type AND
language_id='en'
LEFT JOIN(SELECT product_id, count(product_id)AS counted, MIN(NULLIF(quantity,0))AS minimum FROM invoice
WHERE gender =a_gender AND
client_id=p_client_id AND
product_type = 'LODGING' AND
dateQF BETWEEN date1 AND DATE(DATE_ADD(date2, INTERVAL -1 DAY)) AND
(commit_invoice=1 OR commit_invoice=3) AND
invoice_set !='SYSTEM'
GROUP BY product_id)B
ON B.product_id=p_room_id
ORDER BY counted DESC
LIMIT 1;
END $$
DELIMITER ;

Related

ERROR 1172 (42000): Result consisted of more than one row on mysql stored procedure

I am getting ERROR 1172 (42000): Result consisted of more than one row on my stored procedure when I use CALL submitOrder( 10, 100, 1, #OrderId);
What went wrong in my code? The goal is to use the procedure to insert a new row into the orders table and a new row on the order details into order_items table.
DELIMITER //
CREATE PROCEDURE submitOrder(
IN customerId INT,
IN productId INT,
IN qty INT,
OUT orderId INT)
BEGIN
DECLARE orderId, storeId, staffId, qty, customerId, productId INT;
DECLARE listPrice DECIMAL(10,2);
DECLARE discount DECIMAL(4,2);
SELECT MAX(order_id)+1 FROM orders INTO #orderId;
SELECT s.store_id
FROM stocks AS s
INNER JOIN products AS p USING (product_id)
WHERE p.product_id = s.product_id
ORDER BY s.quantity DESC
LIMIT 1
INTO #storeId;
SELECT staffs.staff_id
FROM staffs
INNER JOIN stores
WHERE staffs.store_id = stores.store_id
LIMIT 1
INTO #staffID;
SET #qty = 1;
SELECT products.product_id
FROM products
WHERE products.product_id = productId;
SELECT products.list_price
FROM products
INTO #listPrice;
INSERT INTO orders VALUES (
#orderId, #customerId, 1, CURDATE(), ADDDATE(CURDATE(), INTERVAL 7 day), NULL, #storeId, #staffId);
INSERT INTO order_items
VALUES (#orderId, 1, #productId, #qty, #listPrice,0);
END//
DELIMITER ;
You forgot to put a WHERE clause and LIMIT 1 into the query that sets $listPrice. You also forgot to set #productId anywhere.
You can do both of these in the same query.
SELECT product_id, list_price
FROM products
WHERE product_id = productId
LIMIT 1
INTO #productId, #listPrice;
I'm not sure why you need the #product_id variable, since it will be the same as productId.

MySQL Attendance Calculation

Here i have table attendances
I need result as shown below
How can i achieve this in mysql without using any programming language
Sql File is Attendances.sql
We can try a pivot query approach, aggregating by user and date:
SELECT
user_id,
DATE(date_time) AS date,
TIMESTAMPDIFF(MINUTE,
MAX(CASE WHEN status = 'IN' THEN date_time END),
MAX(CASE WHEN status = 'OUT' THEN date_time END)) / 60.0 AS hours
FROM yourTable
GROUP BY
user_id,
DATE(date_time);
The caveats of this answer are many. It assumes that there would be only one IN and OUT entry, per user, per day. If a period could cross over dates, then my answer might not generate correct results. Also, if an IN or OUT value be missing, then NULL would be reported for the hours value.
I have Achieve it my self by creating a mysql function and view
Mysql View
CREATE OR REPLACE VIEW `view_attendances` AS
SELECT
`a`.`id` AS `a1_id`,
`a`.`user_id` AS `user_id`,
CAST(`a`.`date_time` AS DATE) AS `date`,
`a`.`date_time` AS `in`,
`a2`.`id` AS `a2_id`,
`a2`.`date_time` AS `out`,
(TIMESTAMPDIFF(SECOND,
`a`.`date_time`,
`a2`.`date_time`) / 3600) AS `hours`
FROM
(`attendances` `a`
JOIN `attendances` `a2` ON (((`a`.`is_confirm` = 1)
AND (`a`.`status` = 'IN')
AND (`a2`.`id` = FN_NEXT_OUT_ATTENDANCE_ID(`a`.`user_id`, `a`.`date_time`, `a`.`status`))
AND (a2.status = 'OUT')
AND (CAST(`a`.`date_time` AS DATE) = CAST(`a2`.`date_time` AS DATE)))))
Mysql Function
CREATE FUNCTION `fn_next_out_attendance_id`( _user_id INT, _attendance_date_time DATETIME, _status VARCHAR(10) ) RETURNS int(11)
BEGIN
DECLARE _id INT(11);
SELECT
id INTO _id
FROM
attendances
WHERE
is_confirm = 1
AND user_id = _user_id
AND date_time > _attendance_date_time
AND `status` <> _status
ORDER BY
date_time ASC LIMIT 1 ;
RETURN if (_id IS NULL, 0, _id);
END

MySQL “subquery returns more than 1 row” error

I am trying to calculate the sum of the products which a client can order based on the product id and the quantity needed.
[CREATE DEFINER=`root`#`localhost` PROCEDURE `calculate_sum`()
BEGIN
SELECT
(SELECT produs_comandat.idComanda
FROM produs_comandat
WHERE produs_comandat.idComanda <= comanda.idComanda) AS idComanda,
(SELECT client.idClient
FROM client
WHERE client.idClient <= comanda.idClient)AS idClient,
(SELECT SUM( produs.pret )
FROM produs_comandat
INNER JOIN produs ON produs.idProdus = produs_comandat.idProdus
WHERE comanda.idComanda <= produs_comandat.idComanda) AS suma
FROM comanda
ORDER BY
(SELECT produs_comandat.cantitate
FROM produs_comandat
WHERE produs_comandat.idComanda <= comanda.idComanda);
END][1]

Calculate Date by number of working days from a certain Startdate

I've got two tables, a project table and a calendar table. The first containts a startdate and days required. The calendar table contains the usual date information, like date, dayofweek, and a column is workingday, which shows if the day is a saturday, sunday, or bank holiday (value = 0) or a regular workday (value = 1).
For a certain report I need write a stored procedure that calculates the predicted enddate by adding the number of estimated workddays needed.
Example:
**Projects**
Name Start_Planned Work_days_Required
Project A 02.05.2016 6
Calendar (04.05 is a bank holdiday)
Day Weekday Workingday
01.05.2016 7 0
02.05.2016 1 1
03.05.2016 2 1
04.05.2016 3 0
05.05.2016 4 1
06.05.2016 5 1
07.05.2016 6 0
08.05.2016 7 0
09.05.2016 1 1
10.05.2016 2 1
Let's say, the estimated number of days required is given as 6 (which leads to the predicted enddate of 10.05.2016). Is it possible to join the tables in a way, which allows me to put something like
select date as enddate_predicted
from calendar
join projects
where number_of_days = 6
I would post some more code, but I'm quite stuck on how where to start.
Thanks!
You could get all working days after your first date, then apply ROW_NUMBER() to get the number of days for each date:
SELECT Date, DayNum = ROW_NUMBER() OVER(ORDER BY Date)
FROM Calendar
WHERE IsWorkingDay = 1
AND Date >= #StartPlanned
Then it would just be a case of filtering for the 6th day:
DECLARE #StartPlanned DATE = '20160502',
#Days INT = 6;
SELECT Date
FROM ( SELECT Date, DayNum = ROW_NUMBER() OVER(ORDER BY Date)
FROM Calendar
WHERE WorkingDay = 1
AND Date >= #StartPlanned
) AS c
WHERE c.DayNum = #Days;
It's not part of the question, but for future proofing this is easier to acheive in SQL Server 2012+ with OFFSET/FETCH
DECLARE #StartPlanned DATE = '20160502',
#Days INT = 6;
SELECT Date
FROM dbo.Calendar
WHERE Date >= #StartPlanned
AND WorkingDay = 1
ORDER BY Date
OFFSET (#Days - 1) ROWS FETCH NEXT 1 ROWS ONLY
ADDENDUM
I missed the part earlier about having another table, and the comment about putting it into a cursor has prompted me to amend my answer. I would add a new column to your calendar table called WorkingDayRank:
ALTER TABLE dbo.Calendar ADD WorkingDayRank INT NULL;
GO
UPDATE c
SET WorkingDayRank = wdr
FROM ( SELECT Date, wdr = ROW_NUMBER() OVER(ORDER BY Date)
FROM dbo.Calendar
WHERE WorkingDay = 1
) AS c;
This can be done on the fly, but you will get better performance with it stored as a value, then your query becomes:
SELECT p.Name,
p.Start_Planned,
p.Work_days_Required,
EndDate = c2.Date
FROM Projects AS P
INNER JOIN dbo.Calendar AS c1
ON c1.Date = p.Start_Planned
INNER JOIN dbo.Calendar AS c2
ON c2.WorkingDayRank = c1.WorkingDayRank + p.Work_days_Required - 1;
This simply gets the working day rank of your start date, and finds the number of days ahead specified by the project by joining on WorkingDayRank (-1 because you want the end date inclusive of the range)
This will fail, if you ever plan to start your project on a non working day though, so a more robust solution might be:
SELECT p.Name,
p.Start_Planned,
p.Work_days_Required,
EndDate = c2.Date
FROM Projects AS P
CROSS APPLY
( SELECT TOP 1 c1.Date, c1.WorkingDayRank
FROM dbo.Calendar AS c1
WHERE c1.Date >= p.Start_Planned
AND c1.WorkingDay = 1
ORDER BY c1.Date
) AS c1
INNER JOIN dbo.Calendar AS c2
ON c2.WorkingDayRank = c1.WorkingDayRank + p.Work_days_Required - 1;
This uses CROSS APPLY to get the next working day on or after your project start date, then applies the same join as before.
This query returns a table with a predicted enddate for each project
select name,min(day) as predicted_enddate from (
select c.day,p.name from dbo.Calendar c
join dbo.Calendar c2 on c.day>=c2.day
join dbo.Projects p on p.start_planned<=c.day and p.start_planned<=c2.day
group by c.day,p.work_days_required,p.name
having sum(c2.workingday)=p.work_days_required
) a
group by name
--This gives me info about all projects
select p.projectname,p.Start_Planned ,c.date,
from calendar c
join
projects o
on c.date=dateadd(days,p.Work_days_Required,p.Start_Planned)
and c.isworkingday=1
now you can use CTE like below or wrap this in a procedure
;with cte
as
(
Select
p.projectnam
p.Start_Planned ,
c.date,datediff(days,p.Start_Planned,c.date) as nooffdays
from calendar c
join
projects o
on c.date=dateadd(days,p.Work_days_Required,p.Start_Planned)
and c.isworkingday=1
)
select * from cte where nooffdays=6
use below logic
CREATE TABLE #proj(Name varchar(50),Start_Planned date,
Work_days_Required int)
insert into #proj
values('Project A','02.05.2016',6)
CReATE TABLE #Calendar(Day date,Weekday int,Workingday bit)
insert into #Calendar
values('01.05.2016',7,0),
('02.05.2016',1,1),
('03.05.2016',2,1),
('04.05.2016',3,0),
('05.05.2016',4,1),
('06.05.2016',5,1),
('07.05.2016',6,0),
('08.05.2016',7,0),
('09.05.2016',1,1),
('10.05.2016',2,1)
DECLARE #req_day int = 3
DECLARE #date date = '02.05.2016'
--SELECT #req_day = Work_days_Required FROM #proj where Start_Planned = #date
select *,row_number() over(order by [day] desc) as cnt
from #Calendar
where Workingday = 1
and [Day] > #date
SELECT *
FROM
(
select *,row_number() over(order by [day] desc) as cnt
from #Calendar
where Workingday = 1
and [Day] > #date
)a
where cnt = #req_day

mysql query uses every row of another query

I have searched a lot, but cannot find a helpful answer:
i want to have a list of totals from a period the user defines by giving me a start and end date. The totals should every time being from the start date to beginning with the start date and add every row 1 day. so the last row gives the totals from start to end date.
example: - given period = start 2013-01-01 , end = 2013-01-31
total day 1 = 100
total day 2 = 0 (not listed in my totalsperday query, but should have a row in my final query)
total day 3 = 140
total day 4 = 20
...
final table should look like:
end day 1: 100
end day 2: 100
end day 3: 240
end day 4: 260
...
so i have a query who calculates all days:
SELECT '2013-01-01' as startdate, w.endDate
FROM
(
SELECT date('2013-01-01' + INTERVAL u.i*100 + v.i*10 + w.i DAY) AS endDate
FROM sysints AS u
JOIN sysints AS v
JOIN sysints AS w
WHERE ( u.i*100 + v.i*10 + w.i ) <=
(
SELECT DATEDIFF( '2013-01-31','2013-01-01') as ddff
)
) w
ORDER BY w.endDate ASC
and i have a query who calculates the totals per day
SELECT p.selldate, SUM(p.price) as totalPerDay
FROM products p
WHERE '2013-01-01' >= p.selldate <= '2013-01-31'
GROUP BY p.selldate
ORDER BY p.selldate ASC
now combining these two to get my final result is hard.
basically what the final query should look like is:
- make the sum of sumperday from day 1 to day 1
- make the sum of sumperday from day 1 to day 2
- make the sum of sumperday from day 1 to day 3
...
any help?
thx.
this is a simplified example of my final query.
Below is the sample. The idea is to obtain a initial data set ordered by date and having aggregate totals, implicit date range records. Then using the cursor you can pass through each row to get the final total column (amountCalc column in the sample) just by summarize the previous records - will work because you already have the columns ordered by date.
The procedure can have other input/ output parameters. Instead of getting info from table you can get data from one view, where the view can be already order by date asc. Is just a sample so can be customized as needed.
Good luck.
-- drop table `Balance`;
CREATE TABLE `Balance` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` DATE NOT NULL,
`account` varchar(30) NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `Balance` (`date`, `account`, `amount`) VALUES
('2013-01-02', 'T355176', 8700),
('2013-01-03', 'T355176', 8900),
('2013-01-04', 'T355215', 33308),
('2013-01-03', 'T355215', 116581),
('2013-01-06', 'T812022', 275000),
('2013-01-02', 'T812063', 136500),
('2013-01-05', 'T812063', 11682),
('2013-01-06', 'T812064', 615100),
('2013-01-03', 'T812064', 25000),
('2013-01-02', 'T812085', 82500);
SELECT * FROM Balance WHERE date >= '2013-01-01' AND date <= '2013-01-06' ORDER BY date ASC;
CALL sp_getTotals('2013-01-01', '2013-01-06');
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `sp_getTotals`(IN startDate DATE, IN endDate DATE)
BEGIN
DECLARE dt DATE;
DECLARE amt DECIMAL(10,2);
DECLARE amtCalcPart DECIMAL(10,2);
DECLARE done INT DEFAULT 0;
DECLARE dtStart DATE;
DECLARE dtEnd DATE;
DECLARE cur1 CURSOR FOR SELECT date, amount FROM `TempMB`;
DECLARE cur2 CURSOR FOR SELECT startDate, endDate;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS `TempMB`;
CREATE TEMPORARY TABLE IF NOT EXISTS `TempMB` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` DATE NOT NULL,
`amount` DECIMAL(10,2) NULL DEFAULT 0.00,
`amountCalc` DECIMAL(10,2) NULL DEFAULT 0.00,
PRIMARY KEY (`id`)
);
SET dtStart = DATE(startDate);
SET dtEnd = DATE(endDate);
WHILE dtStart <= dtEnd DO
INSERT INTO `TempMB` (`date`) SELECT dtStart;
SET dtStart = DATE_ADD(dtStart, INTERVAL 1 DAY);
END WHILE;
SELECT * FROM TempMB;
-- Fill temp table with info needed
UPDATE `TempMB` t
INNER JOIN
(
SELECT date, SUM(amount) AS amount
FROM Balance
WHERE
date >= startDate AND date <= endDate
GROUP BY date
ORDER BY date ASC
) b ON b.date = t.date
SET
t.amount = b.amount;
/*INSERT INTO `TempMB` (`date`, `amount`)
SELECT date, SUM(amount) AS amount
FROM Balance
WHERE
date >= startDate AND date <= endDate
GROUP BY date
ORDER BY date ASC;
*/
SET amtCalcPart = 0.00;
-- Initialise cursor
OPEN cur1;
-- USE BEGIN-END handler for cursor-control within own BEGIN-END block
BEGIN
DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END;
-- Loop cursor throu temp records
LOOP
-- Get next value
FETCH cur1 INTO dt, amt;
-- Calculate amountCalc
SET amtCalcPart = (SELECT SUM(amount) as amt FROM `TempMB` WHERE Date <= dt);
UPDATE `TempMB` SET amountCalc = amtCalcPart WHERE date = dt;
END LOOP;
END;
-- Release cursor
CLOSE cur1;
SELECT * FROM TempMB;
END