PL SQL select count(*) giving wrong answer - sql

I have I table consisting of 3 columns: system, module and block. Table is filled in a procedure which accepts system, module and block and then it checks if the trio is in the table:
select count(*) into any_rows_found from logs_table llt
where system=llt.system and module=llt.module and block=llt.block;
If the table already has a row containing those three values, then don't write them into the table and if it doesn't have them, write them in. The problem is, if the table has values 'system=a module=b block=c' and I query for values 'does the table have system=a module=d block=e' it returns yes, or, to be precise, any_rows_found=1. Value 1 is only not presented when I send a trio that doesn't have one of it's values in the table, for example: 'system=g module=h and block=i'. What is the problem in my query?

Problem is in this:
where system = llt.system
Both systems are the same, it is as if you put where 1 = 1, so Oracle is kind of confused (thanks to you).
What to do? Rename procedure's parameters to e.g. par_system so that query becomes
where llt.system = par_system
Another option (worse, in my opinion) is to precede parameter's name with the procedure name. If procedure's name was e.g. p_test, then you'd have
where llt.system = p_test.system

From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
So when you do
where system=llt.system
that is interpreted as
where llt.system=llt.system
which is always true (unless it's null). It is common to prefix parameters and local variables (e.g. with p_ or l_) to avoid confusion.
So as #Littlefoot said, either change the procedure definition to make the parameter names different to the column names, or qualify the parameter names with the procedure name - which some people prefer but I find more cumbersome, and it's easier to forget and accidentally use the wrong reference.

Root cause is alias used for table name.
where system=llt.system and module=llt.module and block=llt.block;
Table name alias in select query and input to procedure having the same name(i.e. llt
). You should consider either renaming one of them.

Related

How to select value from Oracle table having field name as default keyword

We have Oracle table having default keyword(i.e in as field name) field name.Now i am querying table but unable to extract specific field data.
select a.filename,a.in from table a
Following error appears "invalid field name.
Try using double quotes.
select a."IN" from table a
You can use default (oracle reserved) keywords as the name of the columns but yes it is not advisable to use it.
Anyway, If you want to use oracle reserved keywords then you must have to enclose them in the double-quotes.
Note that oracle is case insensitive in terms of its object names until and unless it is wrapped in the double-quotes. it means if you enclose any object name in double-quotes then you must have to use them anywhere in the entire DB as case sensitive manner.
So if your table definition is:
CREATE TABLE YOUR_TABLE ("IN" NUMBER);
Then you need to use "IN" wherever you want to refer the column but if your table definition is:
CREATE TABLE YOUR_TABLE ("in" NUMBER);
Then you need to use "in" wherever you want to refer the column. -- case sensitive names.
I hope it will clear all your doubts.
Cheers!!

Set default value for WHERE clause for specific columns

Suppose I've created a table like this:
CREATE TABLE stuff(
a1 TEXT,
a2 TEXT,
. . .
an TEXT,
data TEXT
);
I need to make SELECT queries like this:
SELECT data FROM stuff WHERE
a1="..." and a2="..." and . . . and an="..."
selecting a specific value of data, with all a values specified.
There are a lot of as that have some value that I consider default.
Is there a way to add some kind of statement to the table or the query that will use some default value for as which are not explicitly constrained in the where clause? For example, so that if I don't write a1="b" after where I get only rows where a1 is equal to "b", rather than any value, but if I write a1="c" I get those.
The default is the same for all as.
The best solution for me would be to bake the default into the table or the database file.
Is there a way to add some kind of statement to the table or the query
that will use some default value for as which are not explicitly
constrained in the where clause?
Short answer: No.
Based off of what you said here:
For example, so that if I don't write a1="b" after where
I'm thinking you might be running this query often, maybe even manually, and you want to pass in some different values. If this is indeed the case, you can pass parameters in and use some variables to handle this.
What you can do, is have a CASE statement in that WHERE to help. Consider the following:
SELECT *
FROM
table
WHERE
CASE
WHEN %s IS NOT NULL
THEN a1 = %s
ELSE a1 = 'b'
END
[AND/OR] <other constraints here>
Now, the syntax of how you actually do this will vary, based on what you're actually using to execute your queries. Are they being ran from a python program? something in .net? etc etc
Also, you wouldn't have to strictly stick to the IS NOT NULL I used there, you could have some other values to do other things with. Up to you.
EDIT:
To Shawn's point in the comments, if %s, the argument being passed in, is either NOT NULL or a legitmate value, then ifnull() would be a cleaner alternative for this case statement. Example below:
WHERE
a1 = ifnull(%s, 'b')

Asp Classic & Firbird Sql without quotation marks

I have a script in ASP Classic that uses a Firebird database. I would like to execute a query without "quotation marks"
Now I must write this:
SQL = "SELECT ""TArticoli"".""IDArticolo"",""TArticoli"".""Desc"" FROM ""TArticoli"";"
I would write this:
SQL = "SELECT TArticles.IDArticle, TArticles.Desc FROM TArticles;"
The first one is accepted the second not, how can I do this?
You can't. DESC is a reserved word in Firebird, so to be able to use it as a column name (or any other object name for that matter), you will need to enclose it in quotes.
A second problem is that you are currently using
SELECT "TArticoli"."IDArticolo","TArticoli"."Desc" FROM "TArticoli"
And this means both your table name and the column names are case sensitive, and in that case, quoting those object names is mandatory. Unquoted object names are case insensitive, but are mapped to object names in upper case. This means that select * from TArticoli will select from a table called TARTICOLI, while select * from "TArticoli", selects from a table called TArticoli.
So unless you are going to rename or recreate all your tables or columns, you will not be able to get rid of quotes. The only thing you can do to reduce the number of quotes, is by not prefixing the columns with the table names (in the query shown it isn't necessary), or otherwise use a case insensitive alias for the table, eg
SELECT "IDArticolo", "Desc" FROM "TArticoli"
or
SELECT a."IDArticolo", a."Desc" FROM "TArticoli" AS a

Stored procedure that takes list of string as input and insert missing rows then return them

I have a table Names
Id Name
+----+---------------+
1 John
2 Kate
3 Mark
I want to create a stored procedure that does the following:
1) take a list of names as string as an input parameter
I have done some researches about this but couldn't find the best way to do it. I will call the stored procedure from the entity framework in a C# application. I was thinking of passing the names in one string separated with a comma and the split them in the procedure. But can't figure out how this is done.
2) for each name in the list, if the name does not exist in the Name column, insert a new row for it.
How can I do a switch case if it exists and insert it if not
3) Select * rows that are in the input list.
After adding all the missing Names, I want to select all the names that were in the input list with their id
Can this be done in one stored procedure, or do I have to divide them into multiple.
I am looking for hints on how to do each step, and if they can be combined.
Thanks
Keep your DB side lean and leave logic on the app side, especially if you have grumpy DBA's.
Use a MERGE/Upsert instead.
Check out this SO post.
If you pass a comma delimited list into a stored procedure as a parameter, you are going to need to understand how to use charindex, left, substring and right
As you split out each name - you would add them into a temporary table or a table valued variable.
Your decision about whether to insert the new names into the Names table would be made using an exists() subquery on an insert statement.
You could then, finally, fashion a select statement to join your temp table/table valued variable back to your Names table to pull out all of the keys (including the new ones) and pass them back to your front end.

MySQL - Set default value for field as a string concatenation function

I have a table that looks a bit like this actors(forename, surname, stage_name);
I want to update stage_name to have a default value of
forename." ".surname
So that
insert into actors(forename, surname) values ('Stack', 'Overflow');
would produce the record
'Stack' 'Overflow' 'Stack Overflow'
Is this possible?
Thanks :)
MySQL does not support computed columns or expressions in the DEFAULT option of a column definition.
You can do this in a trigger (MySQL 5.0 or greater required):
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END
You may also want to create a similar trigger BEFORE UPDATE.
Watch out for NULL in forename and surname, because concat of a NULL with any other string produces a NULL. Use COALESCE() on each column or on the concatenated string as appropriate.
edit: The following example sets stage_name only if it's NULL. Otherwise you can specify the stage_name in your INSERT statement, and it'll be preserved.
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
IF (NEW.stage_name IS NULL) THEN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END IF;
END
According to 10.1.4. Data Type Default Values no, you can't do that. You can only use a constant or CURRENT_TIMESTAMP.
OTOH if you're pretty up-to-date, you could probably use a trigger to accomplish the same thing.
My first thought is if you have the two values in other fields what is the compelling need for redundantly storing them in a third field? It flies in the face of normalization and efficiency.
If you simply want to store the concatenated value then you can simply create a view (or IMSNHO even better a stored procedure) that concatenates the values into a pseudo actor field and perform your reads from the view/sproc instead of the table directly.
If you absolutely must store the concatenated value you could handle this in two ways:
1) Use a stored procedure to do your inserts instead of straight SQL. This way you can receive the values and construct a value for the field you wish to populate then build the insert statement including a concatenated value for the actors field.
2) So I don't draw too many flames, treat this suggestion with kid gloves. Use only as a last resort. You could hack this behavior by adding a trigger to build the value if it is left null. Generally, triggers are not good. They add unseen cost and interactions to fairly simple interactions. You can, though, use the CREATE TRIGGER to update the actors field after a record is inserted or updated. Here is the reference page.
As of MySQL 8.0.13, you can use DEFAULT clause for a column which can be a literal constant or an expression.
If you want to use an expression then, simply enclose the required expression within parentheses.
(concat(forename," ",surname))
There are two ways to accomplish what you are trying to do as per my knowledge:
(important: consider backing up your table first before running below queries)
1- Drop the column "stage_name" all together and create a new one with DEFAULT constraint.
ALTER TABLE actors ADD COLUMN stage_name VARCHAR(20) DEFAULT (concat(forename," ",surname))
2- This will update newer entries in the column "stage_name" but not the old ones.
ALTER TABLE actors alter stage_name set DEFAULT (concat(forename," ",surname));
After that, if you need to update the previous values in the column "stage_name" then simply run:
UPDATE actors SET stage_name=(concat(forename," ",surname));
I believe this should solve your problem.