Optional clauses in WHERE SQL COBOL - sql

I have a form and each field is optional.
I would like to make an SQL query that includes optional conditions in the WHERE.
I'm using as a programming language COBOL, so i can't concatinate where clauses on the query
Bellow is my Query
SELECT *
FROM TABLE
WHERE Field1 = :VAL1
AND Field2 = :VAL2
How can i fill VAL1 and VAL2 when fields of the form is empty to get all row of the table TABLE

You can add an extra condition in the filtering. For example:
SELECT *
FROM TABLE
WHERE (Field1 = :VAL1 or :VAL1 = '')
AND (Field2 = :VAL2 or :VAL2 = '')
Note: If performance becomes an issue, it can be improved by the use of Dynamic SQL. That is, by dynamically adding only the parameters that are significant. This, of course, requires extra effort on coding and testing.

You could use different queries depending on the values filled from the form.
For example:
IF VAL1 EQUAL SPACES AND VAL2 EQUAL SPACES
EXEC SQL
SELECT *
FROM TABLE
END-EXEC
ELSE IF FIELD1 EQUAL SPACES
EXEC SQL
SELECT *
FROM TABLE
WHERE FIELD2 = :VAL2
END-EXEC
ELSE
EXEC SQL
SELECT *
FROM TABLE
WHERE FIELD1 = :VAL1
END-EXEC
END-IF

You can use SQLCODE =100 here is an example :
EXEC SQL
SELECT *
FROM TABLE
WHERE Field1 = :VAL1
AND Field2 = :VAL2
END-EXEC.
EVALUATE SQLCODE
WHEN ZEROS
CONTINUE
WHEN 100
*fill VAL1 and VAL2
WHEN OTHER
DISPLAY 'ERREUR'
END-EVALUATE.

Related

Convert each parameter to question mark for query statement

I mean from this:
SELECT * FROM my_tab WHERE col1 = 'name1' AND col2 in(1,2,3);
how to get this:
SELECT * FROM my_tab WHERE col1 = ? AND col2 in(?,?,?);
pg_stat_statements does similar but it not saves original(with parameters) statement. I need original query also.
May be there is some open source or regex expression, which does this?
If you have access to Python, you may try:
sql = "SELECT * FROM my_tab WHERE col1 = 'name1' AND col2 IN (1,2,3);"
output = re.sub(r'\bIN\s*\((.*?)\)', lambda m: 'IN (' + re.sub(r'\w+', '?', m.group(1)) + ')', sql)
print(output)
This prints:
SELECT * FROM my_tab WHERE col1 = 'name1' AND col2 IN (?,?,?);
You might be able to do this for specific queries, but a general solution for this question would require parsing the SQL statement, which is complicated, to say the least.
Try to find a different solution for the problem underlying your question.

SQL Injection: or 1=1 vs ' or 1=1; -- -

Trying to learn and understand SQL injection.
Can anyone explain to me why ' or 1=1; -- - allowed me to bypass authentication and or 1=1 did not?
Think of a query that is built using string concatenation:
"select * from myTable where id = '" + txtIdEnteredByUser +"'"
If the end user inputs:
' or 1=1; --
then the query becomes:
select * from myTable where id = '' or 1=1; --'
That is a valid query and always evaluates to true because of the (OR 1=1), as a result the whole table values are returned.
However, if the user input was:
or 1=1;
the query becomes:
select * from myTable where id = ' or 1=1;'
which is query that wouldn't return something (likely).

Dynamic WHERE clause in T-SQL

I have a requirement to display reports if a field value or a combination of field value matches.
I have designed a table to store ReportId values against a field or a combination of fields. I have stored the values in a pattern so that I can construct the WHERE clause for SQL to fetch desired data. Following is the table data:
Field1 Field2 Field3 ReportId
2 NULL NULL Rep1
5 4 NULL Rep2
6 NULL 8 Rep4
Now, I want to create a stored procedure to fetch relevant ReportIds. In the stored procedure I have following parameters as input:
Parameter1, Parameter2, Parameter3
In the stored procedure, I want to construct a SQL with a dynamic WHERE clause to fetch ReportIds. The WHERE clause will put a AND operator between all NON NULL fields and compare with the passed parameters.
As per the given table data, the non-null field in first row is “Field1”. So the WHERE clause will be
Field1 = Parameter1
As per the given table data, the non-null fields in the second row is “Field1” and “Field2”. So WHERE clause will be:
Field1 = Parameter1 AND Field2 =Parameter2
As per the given table data, the non-null fields in the third row is “Field1” and “Field3”. So the WHERE clause will be:
Field1 = Parameter1 AND Field3 =Parameter3
How I can write a generic SQL with a dynamic WHERE clause for this requirement?
Le sigh.
The solution I am thinking of is a simple:
WHERE (Field1 IS NULL OR Field1=#Parameter1)
AND (Field2 IS NULL OR Field2=#Parameter2)
AND ...
You can stack any number of field/parameter pairings into this solution.
You can construct the query by appending the where clause to NVARCHAR(MAX) variable and then execute that query with sql built in function sp_executesql
For Example
DECLARE #query NVARCHAR(MAX);
SET #query = 'SELECT #Count = COUNT(*) FROM Reports WHERE 1=1 '
IF #Field1 IS NOT NULL
SET #query = #query + 'AND Field1 = #Field1 '
......
......
......
EXECUTE sp_executesql #query,
N'#Field1 VARCHAR(MAX),
....
....
#Param1,
.....
.....
I hope this helps you get the right direction.
If you think about what you are looking for and restate your question it becomes easier. What do you really want to do? You want to include rows where each field matches the parameter passed, or whether the value in the table is null. So you want to match rows where each field is either null, or it matches the parameter:
WHERE (Field1 is null OR Field1 = #Parameter1)
AND (Field2 is null OR Field2 = #Parameter2)
AND (Field3 is null OR Field3 = #Parameter2)

How can I copy a record, changing only the id?

My table has a large number of columns. I have a command to copy some data - think of it as cloning a product - but as the columns may change in the future, I would like to only select everything from the table and only change the value of one column without having to refer to the rest.
Eg instead of:
INSERT INTO MYTABLE (
SELECT NEW_ID, COLUMN_1, COLUMN_2, COLUMN_3, etc
FROM MYTABLE)
I would like something resembling
INSERT INTO MYTABLE (
SELECT * {update this, set ID = NEW_ID}
FROM MYTABLE)
Is there a simple way to do this?
This is a DB2 database on an iSeries, but answers for any platform are welcome.
You could do this:
create table mytable_copy as select * from mytable;
update mytable_copy set id=new_id;
insert into mytable select * from mytable_copy;
drop table mytable_copy;
I don't think this is doable entirely within SQL without going to the trouble of creating a temp table. Doing it in memory should be much faster. Beware if you go the temporary table route that you must choose a unique name for your table for each function invocation to avoid the race condition where your code runs twice at the same time and mangles two rows of data into one temp table.
I don't know what kind of language you're using but it should be possible to obtain a list of fields in your program. I would do it like this:
array_of_field_names = conn->get_field__list;
array_of_row_values = conn->execute ("SELECT... ");
array_of_row_values ["ID"] = new_id_value
insert_query_string = "construct insert query string from list of field names and values";
conn->execute (insert_query_string);
Then you can encapsulate that as a function and just call it specifying table, old id and new id and it'd work it's magic.
In Perl code the following snippet would do:
$table_name = "MYTABLE";
$field_name = "ID";
$existing_field_value = "100";
$new_field_value = "101";
my $q = $dbh->prepare ("SELECT * FROM $table_name WHERE $field_name=?");
$q->execute ($existing_field_value);
my $rowdata = $q->fetchrow_hashref; # includes field names
$rowdata->{$field_name} = $new_field_value;
my $insq = $dbh->prepare ("INSERT INTO $table_name (" . join (", ", keys %$rowdata) .
") VALUES (" . join (", ", map { "?" } keys %$rowdata) . ");";
$insq->execute (values %$rowdata);
Hope this helps.
Ok, try this:
declare #othercols nvarchar(max);
declare #qry nvarchar(max);
select #othercols = (
select ', ' + quotename(name)
from sys.columns
where object_id = object_id('tableA')
and name <> 'Field3'
and is_identity = 0
for xml path(''));
select #qry = 'insert mynewtable (changingcol' + #othercols + ') select newval' + #othercols;
exec sp_executesql #qry;
Before you run the "sp_executesql" line, please do "select #qry" to see what the command is that you're going to run.
And of course, you may want to stick this in a stored procedure and pass in a variable instead of the 'Field3' bit.
Rob
Your example should almost work.
Just add the column names of the new table to it.
INSERT INTO MYTABLE
(id, col1, col2)
SELECT new_id,col1, col2
FROM TABLE2
WHERE ...;
i've never worked with db2 but in mssql you could solve it with following procedure. this solution only works if you dont care what new id the items get.
1.) create new table with same scheme but where the id column incrementes automatically. (mssql "identitity specification = 1, identity increment = 1)
2.) than a simple
insert into newTable(col1, col2, col3)
select (col1, col2, col3) from oldatable
should be enough, be sure not to include your id colum in the above statement

creating SQL command to return match or else everything else

i have three checkboxs in my application. If the user ticks a combination of the boxes i want to return matches for the boxes ticked and in the case where a box is not checked i just want to return everything . Can i do this with single SQL command?
I recommend doing the following in the WHERE clause;
...
AND (#OnlyNotApproved = 0 OR ApprovedDate IS NULL)
It is not one SQL command, but works very well for me. Basically the first part checks if the switch is set (checkbox selected). The second is the filter given the checkbox is selected. Here you can do whatever you would normally do.
You can build a SQL statement with a dynamic where clause:
string query = "SELECT * FROM TheTable WHERE 1=1 ";
if (checkBlackOnly.Checked)
query += "AND Color = 'Black' ";
if (checkWhiteOnly.Checked)
query += "AND Color = 'White' ";
Or you can create a stored procedure with variables to do this:
CREATE PROCEDURE dbo.GetList
#CheckBlackOnly bit
, #CheckWhiteOnly bit
AS
SELECT *
FROM TheTable
WHERE
(#CheckBlackOnly = 0 or (#CheckBlackOnly = 1 AND Color = 'Black'))
AND (#CheckWhiteOnly = 0 or (#CheckWhiteOnly = 1 AND Color = 'White'))
....
sure. example below assumes SQL Server but you get the gist.
You could do it pretty easily using some Dynamic SQL
Lets say you were passing your checkboxes to a sproc as bit values.
DECLARE bit #cb1
DECLARE bit #cb2
DECLARE bit #cb3
DECLARE nvarchar(max) #whereClause
IF(#cb1 = 1)
SET #whereClause = #whereClause + ' AND col1 = ' + #cb1
IF(#cb2 = 1)
SET #whereClause = #whereClause + ' AND col2 = ' + #cb2
IF(#cb3 = 1)
SET #whereClause = #whereClause + ' AND col3 = ' + #cb3
DECLARE nvarchar(max) #sql
SET #sql = 'SELECT * FROM Table WHERE 1 = 1' + #whereClause
exec (#sql)
Sure you can.
If you compose your SQL SELECT statement in the code, then you just have to generate:
in case nothing or all is selected (check it using your language), you just issue non-filter version:
SELECT ... FROM ...
in case some checkboxes are checked, you create add a WHERE clause to it:
SELECT ... FROM ... WHERE MyTypeID IN (3, 5, 7)
This is single SQL command, but it is different depending on the selection, of course.
Now, if you would like to use one stored procedure to do the job, then the implementation would depend on the database engine since what you need is to be able to pass multiple parameters. I would discourage using a procedure with just plain 3 parameters, because when you add another check-box, you will have to change the SQL procedure as well.
SELECT *
FROM table
WHERE value IN
(
SELECT option
FROM checked_options
UNION ALL
SELECT option
FROM all_options
WHERE NOT EXISTS (
SELECT 1
FROM checked_options
)
)
The inner subquery will return either the list of the checked options, or all possible options if the list is empty.
For MySQL, it will be better to use this:
SELECT *
FROM t_data
WHERE EXISTS (
SELECT 1
FROM t_checked
WHERE session = 2
)
AND opt IN
(
SELECT opt
FROM t_checked
WHERE session = 2
)
UNION ALL
SELECT *
FROM t_data
WHERE NOT EXISTS (
SELECT 1
FROM t_checked
WHERE session = 2
)
MySQL will notice IMPOSSIBLE WHERE on either of the SELECT's, and will execute only the appropriate one.
See this entry in my blog for performance detail:
Selecting options
If you pass a null into the appropriate values, then it will compare that specific column against itself. If you pass a value, it will compare the column against the value
CREATE PROCEDURE MyCommand
(
#Check1 BIT = NULL,
#Check2 BIT = NULL,
#Check3 BIT = NULL
)
AS
SELECT *
FROM Table
WHERE Column1 = ISNULL(#Check1, Column1)
AND Column2 = ISNULL(#Check2, Column2)
AND Column3 = ISNULL(#Check3, Column3)
The question did not specify a DB product or programming language. However it can be done with ANSI SQL in a cross-product manner.
Assuming a programming language that uses $var$ for variable insertion on strings.
On the server you get all selected values in a list, so if the first two boxes are selected you would have a GET/POST variable like
http://url?colors=black,white
so you build a query like this (pseudocode)
colors = POST['colors'];
colors_list = replace(colors, ',', "','"); // separate colors with single-quotes
sql = "WHERE ('$colors$' == '') OR (color IN ('$colors_list$'));";
and your DB will see:
WHERE ('black,white' == '') OR (color IN ('black','white')); -- some selections
WHERE ('' == '') OR (color IN ('')); -- nothing selected (matches all rows)
Which is a valid SQL query. The first condition matches any row when nothing is selected, otherwise the right side of the OR statement will match any row that is one of the colors. This query scales to an unlimited number of options without modification. The brackets around each clause are optional as well but I use them for clarity.
Naturally you will need to protect the string from SQL injection using parameters or escaping as you see fit. Otherwise a malicious value for colors will allow your DB to be attacked.