I'm attempting to use MySQL cursors for the first time and hitting a wall. Below code snippet should be returning actual (non-midnight) time values for tStart and tEnd, however all I'm getting is '00:00:00'. StartTime and EndTime fields in table are type TIME. Running just the select statement returns the expected values. Num_rows is indicating 1 row returned, which is also expected. Can anyone help with what I'm doing wrong?
DECLARE tStart TIME;
DECLARE tEnd TIME;
DECLARE num_rows INT DEFAULT 0;
DECLARE curs_1 CURSOR FOR SELECT MIN(asd.StartTime), MAX(asd.EndTime) FROM database.table1 asd WHERE (((asd.varid)='1006') AND ((asd.neededdate)='2016-06-22') AND ((asd.neededint)=3));
OPEN curs_1;
SELECT FOUND_ROWS() INTO num_rows;
FETCH curs_1 INTO tStart, tEnd;
Below code can generate test data:
CREATE TABLE `table1` (`StartTime` TIME DEFAULT NULL,`EndTime` TIME DEFAULT NULL, `varid` VARCHAR(10) DEFAULT NULL, `neededdate` DATE DEFAULT NULL, `neededint` INT(11) DEFAULT NULL) ENGINE=INNODB;
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1006','2016-06-22',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1007','2016-06-21',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1008','2016-06-20',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('08:00:00','17:00:00','1006','2016-06-21',2);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('11:00:00','20:00:00','1006','2016-06-22',1);
Your cursor is indeed housing a single row.
You are using FOUND_ROWS() which can work fine without the use of SQL_CALC_FOUND_ROWS but typically uses it. But in your case you will only ever get back 1 row due to your aggregate (sum). So it almost makes no sense. There is nothing to "cursor" over like a tricky set of data.
I see no sense in using a CURSOR. In fact I recommend never ever using a cursor. Performance is awful for a highly tuned SQL Server that has relations and indexes and performance strategies to deliver results. Cursors as like throwing low-performance mechanisms into an otherwise deliberate stream-lines robust engine. Like opening the carburetor on a Corvette and throwing in a handful of sand. They are something new developers to SQL think of as they procedurally try to wrap their heads around their problem. I recommend you never use it.
The following is the Manual Page for Cursors. You need a LOOP construct to use them because you are row by row on your own terms moving through the data in that cursor. It takes quite a bit of patience to ever get them right. Try to use it sparingly, like maybe 1 time per app at most and with the trickiest of code until you solve it otherwise. Performance is really dreadful, trust me.
What you could do is simply the following to get those values into variables.
SELECT MIN(asd.StartTime) , MAX(asd.EndTime) into #myMin,#myMax
FROM table1 asd
WHERE (((asd.varid)='1006') AND ((asd.neededdate)='2016-06-22') AND ((asd.neededint)=3));
select #myMin,#myMax; -- display them if you want
Good luck, and did I mention yet not to use Cursors? :p
Related
SQL Query Forum 20210309
I’m building a Firebase Functions application that talks to a Google Cloud SQL database running MySQL 5.7. I’m trying to retrieve a value from a row in one table and, if it exists (the row or the value), insert a record in a different table.
Based on some examples I found online, my code looks like this:
DECLARE meeting_link varchar(2048) DEFAULT ""; SELECT meeting_link from campaigns where id=2 INTO meeting_link; IF LENGTH(meeting_link) > 0 THEN INSERT INTO clicks (target_id, ip_address, user_agent) VALUES (38, "ip-address", "user-agent") END IF;
In all of the different versions of this I tried, I get an error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE meeting_link varchar(2048) DEFAULT ""; SELECT meeting_link from campaigns' at line 1
Looking around some more, I found posts that say I can’t use DECLARE in anything but stored procedures, but I can use local variables (#ml for example) but I can’t seem to get that working correctly either.
Can someone please help me with the SQL I need for this? I need to create the record only if the record in the query exists and return the meeting_link value to my calling program.
Refer to this documentation page:
https://dev.mysql.com/doc/refman/8.0/en/sql-compound-statements.html
This section describes the syntax for the BEGIN ... END compound statement and other statements that can be used in the body of stored programs
That includes DECLARE and also IF/THEN/ELSE/END constructions. You can't use those outside of stored routines.
Here's a trick you can use instead of a stored routine:
SELECT meeting_link from campaigns where id=2 INTO #meeting_link;
INSERT INTO clicks (target_id, ip_address, user_agent)
SELECT 38, 'ip-address', 'user-agent' FROM dual WHERE LENGTH(#meeting_link) > 0;
You can use #meeting_link which is a user-defined variable, not a declared local variable.
Then instead of using IF, use FROM dual WHERE ... for your condition. The dual table is normally a pseudo-table that doesn't really exist but querying it returns 1 row. But you can make that zero rows if the condition in the WHERE clause isn't satisfied.
So the INSERT will conditionally create either one or zero rows.
Re your comment:
If you need this to be done in a single SQL statement, then one option would be to make that single SQL statement CALL a procedure that you create in your MySQL database. Then at least you could use DECLARE and IF like you were intending.
Another alternative is to combine the two statements I show above like this:
INSERT INTO clicks (target_id, ip_address, user_agent)
SELECT 38, 'ip-address', 'user-agent'
FROM campaigns WHERE id=2 AND LENGTH(meeting_link) > 0;
This works because even though you query the campaigns table, it's not mandatory to select the columns of that table. You can select constant values instead. The conditions in the WHERE clause will make this return either one row or zero rows.
I mean something like:
create table Measures (
id_user int,
date timestamp,
measure_1 double default 'select measure_1 from Measures where data = '**/**/****'',
measure_2 double default 'select measure_1 from Measures where data = '**/**/****'');
In this way I insert the value of the last measure saved in the db..
Is it possible?
Not directly:
11.7 Data Type Default Values
... the default value must be a constant; it cannot be a function or an expression.
You'll have to do this on application level, or in a trigger as suggested by #Timekiller.
You can do that via a before-insert trigger.
Check if NEW.measure_1 is null, and if it is, then perform select and store results.
UPD:
Right, I was in a bit of a hurry yesterday, and forgot to give an example later. Trigger is a good replacement for complex default value - it will work transparently, will look just like the default value from database user standpoint, and you won't have to do anything on the application level, since triggers are stored in the database itself. It will look something like this:
CREATE TRIGGER `measures_bi_trigger` BEFORE INSERT ON `Measures`
FOR EACH ROW BEGIN
if NEW.measure_1 is null then
SET NEW.measure_1 = (select measure_1 from Measures where ... limit 1);
end if;
if NEW.measure_2 is null then
SET NEW.measure_2 = (select measure_2 from Measures where ... limit 1);
end if;
END
It's not exactly clear what should be in your where condition, so you'll have to substitute ... yourself. Note that your query should return exactly one row, so either use an aggregate function like MAX or order by ... limit 1. If your query returns no rows, NULL will be inserted.
I'm converting a ColdFusion Project from Oracle 11 to MS SQL 2008. I used SSMA to convert the DB including triggers, procedures and functions. Sequences were mapped to IDENTITY columns.
I planned on using INSERT-Statements like
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id
values('val1', 'val2')
This throws an error since the table has a trigger defined, that AFTER INSERT writes some of the INSERTED data to another table to keep a history of the data.
Microsoft writes:
If the OUTPUT clause is specified without also specifying the INTO
keyword, the target of the DML operation cannot have any enabled
trigger defined on it for the given DML action. For example, if the
OUTPUT clause is defined in an UPDATE statement, the target table
cannot have any enabled UPDATE triggers.
http://msdn.microsoft.com/en-us/library/ms177564.aspx
I'm now wondering what is the best practice fo firstly retrieve the generated id and secondly to "backup" the INSERTED data in a second table.
Is this a good approach for the INSERT? It works because the INSERTED value is not simply returned but written INTO a temporary variable. It works in my tests as Microsoft describes without throwing an error regarding the trigger.
<cfquery>
DECLARE #tab table(id int);
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id INTO #tab
values('val1', 'val2');
SELECT id FROM #tab;
</cfquery>
Should I use the OUTPUT clause at all? When I have to write multiple clauses in one cfquery-block, shouldn't I better use SELECT SCOPE_DENTITY() ?
Thanks and best,
Bernhard
I think this is what you want to do:
<cfquery name="qryInsert" datasource="db" RESULT="qryResults">
INSERT INTO mytable (col1, col2)
</cfquery>
<cfset id = qryResults.IDENTITYCOL>
This seems to work - the row gets inserted, the instead of trigger returns the result, the after trigger doesn't interfere, and the after trigger logs to the table as expected:
CREATE TABLE dbo.x1(ID INT IDENTITY(1,1), x SYSNAME);
CREATE TABLE dbo.log_after(ID INT, x SYSNAME,
dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);
GO
CREATE TRIGGER dbo.x1_after
ON dbo.x1
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.log_after(x) SELECT x FROM inserted;
END
GO
CREATE TRIGGER dbo.x1_before
ON dbo.x1
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #tab TABLE(id INT);
INSERT dbo.x1(x)
OUTPUT inserted.ID INTO #tab
SELECT x FROM inserted;
SELECT id FROM #tab;
END
GO
Now, if you write this in your cfquery, you should get a row back in output. I'm not CF-savvy so I'm not sure if it has to see some kind of select to know that it will be returning a result set (but you can try it in Management Studio to confirm I am not pulling your leg):
INSERT dbo.x1(x) SELECT N'foo';
Now you should just move your after insert logic to this trigger as well.
Be aware that right now you will get multiple rows back for (which is slightly different from the single result you would get from SCOPE_IDENTITY()). This is a good thing, I just wanted to point it out.
I have to admit that's the first time I've seen someone use a merged approach like that instead of simply using the built-in PK retrieval and splitting it into separate database requests (example).
Ok, First off, I am not a mysql guru. Second, I did search, but saw nothing relevant related to mysql, and since my DB knowledge is limited, guessing syntactical differences between two different Database types just isn't in the cards.
I am trying to determine if a particular value already exists in a table before inserting a row. I've decided to go about this using two Stored procedures. The first:
CREATE PROCEDURE `nExists` ( n VARCHAR(255) ) BEGIN
SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) as T;
END
And The Second:
CREATE PROCEDURE `createUser` ( n VARCHAR(255) ) BEGIN
IF (nExists(n) = 0) THEN
INSERT INTO Users...
END IF;
END
So, as you can see, I'm attempting to call nExists from createUser. I get the error that no Function exists with the name nExists...because it's a stored procedure. I'm not clear on what the difference is, or why such a difference would be necessary, but I'm a Java dev, so maybe I'm missing some grand DB-related concept here.
Could you guys help me out by any chance?
Thanks
I'm not sure how it helped you, but...
why SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) and not just SELECT COUNT(*) FROM Users WHERE username=n?
Just make the user name (or whatever the primary application index is) a UNIQUE index and then there is no need to test: Just try to insert a new record. If it already exists, handle the error. If it succeeds, all is well.
It can (and should) all be one stored procedure.
To be honest, I'm feeling pretty stupid right now. But this simply isn't working.
Scenario
I have a stored procedure that includes an output parameter. I'm trying to SELECT a value INTO that parameter. This seems simple, but it continues giving me faulty results. I've checked many online sources, and I'm certain that I'm trying to do it properly.
Code
DELIMITER //
CREATE PROCEDURE `spGetId`(
IN ParamA VARCHAR(32),
OUT OutputId INT
)
BEGIN
SELECT `id` INTO OutputId
FROM `Table`
WHERE `column_a` = ParamA;
END//
CALL spGetId('foobar', #Bloop)//
SELECT #Bloop//
Results
I have two rows in this table, their IDs being '1' and '2'. The result I get back is '31', whether the SELECT statement matches anything or not.
I have tried many variations, including removing the WHERE clause entirely and having the SELECT return a COUNT(1) into the parameter (which gives me a result of '32', despite there being only 2 rows), and I have tried "declaring" the #Bloop variable before using it in the sproc call by using SET #Bloop = 0.
If you have any insight on why this is happening, and what I can do to make it return the proper value, I would be much obliged. Also, if you can show me how to achieve the same desired result using a Stored Function instead, with a return value, I'd appreciate that even more! My desired approach is using a stored function, but I had similar problems with that, then gave up and tried using a stored proc, only to find I was getting similar results.
Anything you can offer would be helpful!
Edit:
CREATE TABLE `Table` (
`id` int(11) NOT NULL auto_increment,
`column_a` varchar(32) character set utf8 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
mysql> SELECT * FROM Table;
+------+----------+
| id | column_a |
+------+----------+
| 1 | asdf |
| 2 | foobar |
+------+----------+
When I call spGetId() with any argument, it returns the value '31' (even if the argument is 'foobar', which should return an integer value of '2' (or ascii 0x32)). If I modify spGetId() to return the total rowcount of Table, instead of returning '2', it returns '32'.
Your stored proc is working. I think it is returning the ascii value of the character '1' instead of the integer value 1.
I need to learn to vary my testing environments.
I'm still not sure exactly what the problem was, but it looks like phpMyAdmin was performing some kind of type conversion of its own, and I had been running all my tests through that particular client.
Throwing together a quick PHP script of my own and manually calling the sproc (and in further testing, calling a stored function as well) provided the desired results.
So, lesson learned: don't ever trust the client. Got to remember to switch it up a bit.