Assertion in MySQL - sql

I have a SQL script to run against a large database. I'd like to put a couple of simple queries at the start, just as a sanity check.
Is there any way to write an assertion in MySQL? Or any kind of "select ..., and if it doesn't match this value, then abort the entire script"?

Some crazy code. Main point is: SET could raise error for mysql variables.
For example.
SET #value = 0;
SET SESSION sql_mode = if(#value, ##SESSION.sql_mode, 'something wrong uphere');
Would output ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of 'something wrong uphere' and execution would be stopped.
This approach is not semantic but it works.

You could put the entire script in an if statement, depending on what kind of value you need to check, here's an example:
DECLARE #value int
SET #value = (SELECT COUNT(*) FROM dbo)
IF #value >0
BEGIN
--Do Stuff Here
END

You can also do this via a stored procedure / function, as in the example below:
CREATE FUNCTION `RunProcess`() RETURNS INT(11)
runProcess:BEGIN
DECLARE check_value INT;
DECLARE error_code INT;
SELECT COUNT(*) FROM dbo INTO check_value;
IF check_value = 0 THEN set error_code = 666;
LEAVE runProcess;
RETURN error_code;
END IF;
...
...
END;

One way to go about this could be to make an insert fail on purpose if you assertion fails. This is not pretty, but it is simple and it works.
Let's say Table1 contains columnA and columnB. You want to insert into this table the values ('any', 'thing'), but ONLY if these values fulfil some conditions.
Then do something like this:
SELECT 'any', 'thing' INTO #setA, #setB FROM DUAL WHERE ( your conditions...);
INSERT INTO Table1 (columnA, columnB) VALUES (#setA, #setB);
the 'any' and 'thing' values would obviously be inserted at runtime in the query.
If you Table1 is set up to accept only non-null values in column A or B, this will fail when your conditions fail.
A very simple nonsensical example (this obviously can be achieved otherwise, this is only an example :-) ), if you want the values to differ:
SELECT 'any', 'thing' INTO #setA, #setB FROM DUAL WHERE ( 'a'!='b');
INSERT INTO Table1 (columnA, columnB) VALUES (#setA, #setB);
The test condition doesn't have to be on the values you are trying to insert:
SELECT 'any', 'thing' INTO #setA, #setB FROM DUAL WHERE ( 'another' IS NOT NULL);
INSERT INTO Table1 (columnA, columnB) VALUES (#setA, #setB);
If your goal is not to do perform an insert, the same logic can be applied on different transaction types, the idea being to alter the variables so they make the query fail.
Or you can create a dummy table just for this purpose, with a non-null column, and always insert your assertion result in the dummy table's first row. Not pretty, but it works :-)

Related

How can I use a postgres variable in my SELECT statement?

I'm storing a value in a variable featureId and then trying to use that value in my SELECT statement, but postgres seems to be taking the name literally and looking for a column called "featureid". I'm getting an error "ERROR: column "featureid" does not exist
LINE 4: featureId,"
My code is below. How can I use the value of the variable in my SELECT statement?
SELECT id INTO featureId FROM tableA WHERE NAME = 'some value';
INSERT INTO tableB (client_id, feature_id, does_have)
SELECT
id,
featureId,
TRUE
FROM tableA
Without a declared variable your SELECT INTO is the version of SELECT INTO that creates a table. To see it for yourself try:
SELECT id
INTO featureid
FROM tablea
WHERE name = 'some value';
SELECT *
FROM featureid;
For assigning the value to a variable the variable must be declared. You can use an anonymous DO block.
DO
$$
DECLARE
featureid tablea.id%TYPE;
BEGIN
SELECT id
INTO featureid
FROM tablea
WHERE name = 'some value';
INSERT INTO tableb
(client_id,
feature_id,
does_have)
SELECT id,
featureid,
true
FROM tablea;
END;
$$
LANGUAGE plpgsql;
There are few errors on what you're tryng to do:
sql is declarative language so you're asking what to do not how to do and this is for this reason that you cannot store variables and some statements like declare and begin-end should be used in trigger and not in a simple query.
you are executing two statements: select and insert into and they are executed one after the other, so once again you cannot store a variable.
insert into, insert a single record but potentially you're tryng to retrieve more data with your select statement (if NAME is not unique)
if 'some-value' is a known constant and NAME is unique just insert that value in the where clause of the insert into. If you're tryng to insert more data take a look on bulk insert syntax of postgres: bulk insert

How to selectively return rows inside a stored procedure on SQL Server?

I have a base stored procedure simply returning a select from the database, like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
SELECT * FROM MyTable
END
GO
But now I need to execute some logic for every row of my select. According to the result I need to return or not this row. I would have my select statement running with a cursor, checking the rule and return or not the row. Something like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
DECLARE CURSOR_MYCURSOR FOR SELECT Id, Name FROM MyTable
OPEN CURSOR_MYCURSOR
FETCH NEXT FROM CURSOR_MYCURSOR INTO #OUTPUT1, #OUTPUT2
WHILE (##FETCH_STATUS=0)
BEGIN
IF (SOME_CHECK)
SELECT #OUTPUT1, #OUTPUT2
ELSE
--WILL RETURN SOMETHING ELSE
END
END
GO
The first problem is that everytime I do SELECT #OUTPUT1, #OUTPUT2 the rows are sent back as different result sets and not in a single table as I would need.
Sure, applying some logic to a row sounds like a "FUNCTION" job. But I can't use the result of the function to filter the results being selected. That is because when my check returns false I need to select something else to replace the faulty row. So, I need to return the faulty rows so I can be aware of them and replace by some other row.
The other problem with this method is that I would need to declare quite a few variables so that I can output them through the cursor iteration. And those variables would need to follow the data types for the original table attributes and somehow not getting out of sync if something changes on the original tables.
So, what is the best approach to return a single result set based on a criteria?
Thanks in advance.
I recommend use of cursors but easy solution to your question would be to use table variable or temp table
DECLARE #MyTable TABLE
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
CREATE TABLE #MyTable
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
than inside your cursors you can insert records that match your logic
INSERT INTO #MyTable VALUES (#Output1, #Output2)
INSERT INTO #MyTable VALUES (#Output1, #Output2)
after you done with cursor just select everything from table
SELECT * FROM #MyTable
SELECT * FROM #MyTable

UPSERT in SQLite

I don't want to use REPLACE INTO because it's basically a DELETE and INSERT and it's complicated to use the data from the old columns.
INSERT OR IGNORE is a bit of a hack because all errors are ignored, so this is not an option.
I've read a blog article which uses the following:
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
IF ##ROWCOUNT=0
INSERT INTO Table1 VALUES (...)
I like this approach really much, but I don't know how I can implement this IF-clause with the ##ROWCOUNT in SQLite, this is what I got:
SELECT CASE (SELECT
CASE
WHEN EXISTS(SELECT 1 FROM t WHERE id=2)
THEN 1
ELSE 0
END)
WHEN 1
UPDATE t set a='pdf' WHERE id=2;
ELSE
INSERT INTO t (a) VALUES ('pdf');
END
the SELECT CASE seems to be the only way to use a CASE-clause in SQLite because everything else throws an syntax error. But it's also not possible to use a UPDATE- or INSERT-statement in the SELECT CASE, so this throws an error.
I've tried the following
UPDATE t set a='pdf' WHERE id=2;
CASE WHEN (changes()=0) THEN
INSERT INTO t (a) VALUES ('pdf');
END
but this doesn't work, because the CASE-clause throws an syntax error.
can someone provide an example using ##ROWCOUNT for an UPSERT in SQLite?
SQLite has no built-in UPSERT-like statement that doesn't delete the old record.
You should check the number of changes in your program, and execute the INSERT conditionally.
However, if you really want to do this in SQL, it's possible; but you have to use the INSERT ... SELECT ... form so that you are able to insert zero records, if needed:
BEGIN;
UPDATE t SET a = 'pdf' WHERE id = 2;
INSERT INTO t(id, a) SELECT 2, 'pdf' WHERE changes() = 0;
COMMIT;
You should use sqlite API in this case and write "IF logic" in your application.
sqlite3_prepare16_v2(stmt1, "UPDATE t SET a=? WHERE id=?");
sqlite3_prepare16_v2(stmt2, "INSERT INTO t(id, a) VALUES(?, ?)");
for(...) // iterate by rows to be updated/inserted
{
//set parameter values for stmt1
sqlite3_step(stmt1);
if( !sqlite3_changes(dbh) )
{
//set parameter values for stmt2
sqlite3_step(stmt2);
}
}

Get the count of rows in a stored procedure result regardless of number/type of columns

I have developed the following code:
CREATE PROCEDURE [dbo].[Test01]
AS
BEGIN
SELECT * FROM TestTable
END
CREATE PROCEDURE [dbo].[Test02]
AS
BEGIN
DECLARE #tmp TABLE
(
TestID int,
Test nvarchar(100),
)
INSERT INTO #tmp
EXEC Test01
SELECT COUNT(*) FROM #tmp
END
But if I add or remove a column on TestTable I must to modify #tmp otherwise the result is:
Column name or number of supplied values does not match table definition
How can I solve this problem?
Try specify the columns manually:
SELECT a, b FROM TestTable
and
INSERT INTO #tmp (a, b)
This should fix the error you've mentioned.
My first comment would be that SELECT * is frowned upon unless you actually know what you are doing. I would strongly advise against it in your particular use case, precisely because it can get you into the trouble you have identified.
In your particular case, for the specific SP written, you have not used SET NOCOUNT ON, so you can retrieve the count using
SELECT ##ROWCOUNT
But then as Martin has commented, that's just some sample code you threw together. Otherwise, why even use 2 SPs.

Setting multiple scalar variables from a single row in SQL Server 2008?

In a trigger, I have code like:
SET #var1 = (SELECT col1 FROM Inserted);
SET #var2 = (SELECT col2 FROM Inserted);
Is it possible to write the above in a single line? Something conceptually like:
SET (#var1,#var2) = (SELECT col1,col2 FROM Inserted);
Obviously I tried the above, without success; am I just stuck with the first method?
Even if possible, is that a good idea?
Thanks!
yes, use first method.
Or...
SELECT
#var1 = col1
,#var2 = col2
FROM
Inserted;
However, it is a major red flag if you are expecting to set variable values like that in a trigger. It generally means the trigger is poorly designed and needs revision. This code expects there will be only one record in inserted and this is something that is not going to be true in all cases. A multiple record insert or update will have multiple records in inserted and the trigger must account for that (please without using a trigger!!!). Triggers should under no circumstances be written to handle only one-record inserts/updates or deletes. They must be written to handle sets of data.
Example to insert the values from inserted to another table where the trigger is on table1:
CREATE TRIGGER mytrigger on table1
AFTER INSERT
AS
INSERT table2 (field1, field2, field3)
SELECT field1, 'test', CASE WHEN field3 >10 THEN field3 ELSE 0 END
FROM inserted
No, it is not possible. SET accepts a single target and value. AFAIK.