Is there a way to check if one bit is set and another is not at the same time? - sql

Sorry this has probably been asked before but it's rather hard to search for. I'm trying to figure if some bits have been set and some have not been set in a single operation. Is this possible?
e.g. I want to check if the fifth bit is off but either the second or third bit are on.

For the SQL situation you suggested, you could just mask out the three bits and perform an IN check for the patterns you accept, e.g.
SELECT * FROM table
WHERE (field & 0x16) IN (0x2, 0x4);
or if you accept both second and third bits being set at once, you can just do a range check, since all three combinations you accept exhibit no overlap with the range of the not accepted options:
SELECT * FROM table
WHERE (field & 0x16) BETWEEN 0x2 AND 0x6;
I'll note that while this works, it's not great style for SQL (or any language really). In practice, you'd probably want to stick with named single BIT (NOT NULL) fields, which provides more information to SQL (potentially allowing indexed searches and the like). Writing
SELECT * FROM table
WHERE (field2 = 1 OR field3 = 1) AND field5 = 0;
-- or for mutually exclusive
WHERE field2 + field3 = 1 AND field5 = 0;
is not meaningfully longer, and it's significantly more clear what you're doing (assuming you have more useful field names of course).

Related

Can 2 character length variables cause SQL injection vulnerability?

I am taking a text input from the user, then converting it into 2 character length strings (2-Grams)
For example
RX480 becomes
"rx","x4","48","80"
Now if I directly query server like below can they somehow make SQL injection?
select *
from myTable
where myVariable in ('rx', 'x4', '48', '80')
SQL injection is not a matter of length of anything.
It happens when someone adds code to your existing query. They do this by sending in the malicious extra code as a form submission (or something). When your SQL code executes, it doesn't realize that there are more than one thing to do. It just executes what it's told.
You could start with a simple query like:
select *
from thisTable
where something=$something
So you could end up with a query that looks like:
select *
from thisTable
where something=; DROP TABLE employees;
This is an odd example. But it does more or less show why it's dangerous. The first query will fail, but who cares? The second one will actually work. And if you have a table named "employees", well, you don't anymore.
Two characters in this case are sufficient to make an error in query and possibly reveal some information about it. For example try to use string ')480 and watch how your application will behave.
Although not much of an answer, this really doesn't fit in a comment.
Your code scans a table checking to see if a column value matches any pair of consecutive characters from a user supplied string. Expressed in another way:
declare #SearchString as VarChar(10) = 'Voot';
select Buffer, case
when DataLength( Buffer ) != 2 then 0 -- NB: Len() right trims.
when PatIndex( '%' + Buffer + '%', #SearchString ) != 0 then 1
else 0 end as Match
from ( values
( 'vo' ), ( 'go' ), ( 'n ' ), ( 'po' ), ( 'et' ), ( 'ry' ),
( 'oo' ) ) as Samples( Buffer );
In this case you could simply pass the value of #SearchString as a parameter and avoid the issue of the IN clause.
Alternatively, the character pairs could be passed as a table parameter and used with IN: where Buffer in ( select CharacterPair from #CharacterPairs ).
As far as SQL injection goes, limiting the text to character pairs does preclude adding complete statements. It does, as others have noted, allow for corrupting the query and causing it to fail. That, in my mind, constitutes a problem.
I'm still trying to imagine a use-case for this rather odd pattern matching. It won't match a column value longer (or shorter) than two characters against a search string.
There definitely should be a canonical answer to all these innumerable "if I have [some special kind of data treatment] will be my query still vulnerable?" questions.
First of all you should ask yourself - why you are looking to buy yourself such an indulgence? What is the reason? Why do you want add an exception to your data processing? Why separate your data into the sheep and the goats, telling yourself "this data is "safe", I won't process it properly and that data is unsafe, I'll have to do something?
The only reason why such a question could even appear is your application architecture. Or, rather, lack of architecture. Because only in spaghetti code, where user input is added directly to the query, such a question can be ever occur. Otherwise, your database layer should be able to process any kind of data, being totally ignorant of its nature, origin or alleged "safety".

In SQL is there any performance between using like vs equals?

I am using T-SQL (SQL Server).
This is the starting code:
DECLARE #code as char(8)
SET #code = '123' -- 123%'
DECLARE #lsUseLikeForCodeWhereCondition char(1)
IF CHARINDEX('%', #code) = 0
SET #lsUseLikeForCodeWhereCondition = 'N'
ELSE
SET #lsUseLikeForCodeWhereCondition = 'Y'
Is there any performance between these two statements:
select * from mytable where idn_orn_i LIKE
CASE WHEN #lsUseLikeForCodeWhereCondition = 'N' THEN
#code
ELSE
#code + '%'
END
vs
IF #lsUseLikeForCodeWhereCondition = 'N'
BEGIN
select * from mytable where idn_orn_i = #code
END
ELSE
BEGIN
select * from mytable where idn_orn_i Like #code + '%'
END
Both appear to return the same results. But where it says mytable. It is actually a join with 10 different tables. So it is not small. Mostly I am wondering if the optimizer would recognize a like WITHOUT a percent sign in the string and do an equals.
If it matters idn_orn_i is char(8).
The two versions are very different and this has little to do with any differences between like and =. In the first, the pattern is an expression-based pattern, which precludes the use of indexes for the query.
In the second, you have two queries and in each, the comparisons are using constants. So, the first version will definitely use an appropriate index. And I think the second will also take advantage of an index too (the compiler should turn the constant expression into a constant so an index can be used). Note that like requires that the pattern not start with a wildcard and probably has some conditions on the collations as well.
Short answer, if you can use the =, use the =.
If the same number of rows is being returned, it should be the same cost.
The actual execution plans should more or less be equal. That being said, if you are saying where value = 'asdf' vs value like 'asdf%' and you have have a lot more rows that will be returned with the like vs the =, then the = would be faster. It would also vary based on the number of rows that need to be scanned/looked up. Which depends on your data distribution statistics or cardinality.
You should look at the execution plans to know for sure.
Exact matches, =, tend to perform faster because the string search can be potentially invalidated from a check with just the first and/or last characters.
A like cannot discount the match from the first character if it is non-matching, for cases when a % is placed before the pattern of interest. It would search the string sequentially, potentially to the end of the string, until the match is found or not at all. Thus, is more time consuming, but useful and necessary for certain tasks.
A like, without any wild cards, would operate in the same manner as =, but the procedure for = is intrinsically more efficient.

SQL not finding results

This query currently is returning no results, and it should. Can you see anything wrong with this query
field title are NEED_2_TARGET, ID, and CARD
NEED_2_TARGET = integer
CARD = string
ID = integer
value of name is 'Ash Imp'
{this will check if a second target is needed}
//**************************************************************************
function TFGame.checkIf2ndTargetIsNeeded(name: string):integer;
//**************************************************************************
var
targetType : integer; //1 is TCard , 2 is TMana , 0 is no second target needed.
begin
TargetType := 0;
Result := targetType;
with adoquery2 do
begin
close;
sql.Clear;
sql.Add('SELECT * FROM Spells WHERE CARD = '''+name+''' and NEED_2_TARGET = 1');
open;
end;
if adoquery2.RecordCount < 1 then
Result := 0
else
begin
Adoquery2.First;
TargetType := adoquery2.FieldByName(FIELD_TARGET_TYPE).AsInteger;
result := TargetType;
end;
end;
sql db looks like below
ID CARD TRIGGER_NUMBER CATEGORY_NUMBER QUANTITY TARGET_NUMBER TYPE_NUMBER PLUS_NUMBER PERCENT STAT_TARGET_NUMBER REPLACEMENT_CARD_NUMBER MAX_RANDOM LIFE_TO_ADD REPLACED_DAMAGE NEED_2_TARGET TYPE_OF_TARGET
27 Ash Imp 2 2 15 14 1 1
There are a number of things that could be going wrong.
First and most important in your trouble-shooting is to take your query and run it directly against your database. I.e. first confirm your query is correct by eliminating possibilities of other things going wrong. More things confirmed working, the less "noise" to distract you from solving the problem.
As others having pointed out if you're not clearing your SQL statement, you could be returning zero rows in your first result set.
Yes I know, you've since commented that you are clearing your previous query. The point is: if you're having trouble solving your problem, how can you be sure where the problem lies? So, don't leave out potentially relevant information!
Which bring us neatly to the second possibility. I can't see the rest of your code, so I have to ask: are you refreshing your data after changing your query? If you don't Close and Open your query, you may be looking at a previous execution's result set.
I'm unsure whether you're even allowed to change your query text while the component is Active, or even whether that depends on exactly which data access component you're using. The point is, it's worth checking.
Is your application connecting to the correct database? Since you're using Access, it's very easy to be connected to a different database file without realising it.
You can check this by changing your query to return all rows (i.e. delete the WHERE clause).
You my want to change the quotes used in your SQL query. Instead of: ...CARD = "'+name+'" ORDER... rather use ...CARD = '''+name+''' ORDER...
As far as I'm aware single quotes is the ANSI standard. Even if some databases permit double quotes, using them limits portability, and may produce unexpected results when passed through certain data access drivers.
Check the datatype of your CARD column. If it's a fixed length string, then the data values will be padded. E.g. if CARD is char(10), then you might actually need to look for 'Ash Imp '.
Similarly, the actual value may contain spaces before / after the words. Use select without WHERE and check the actual value of the column. You could also check whether SELECT * FROM Spells WHERE CARD LIKE '%Ash Imp%' works.
Finally, as others have suggested, you're better off using a parameterised query rather dynamically building the query up yourself.
Your code will be more readable and flexible.
You can make your code strongly typed; and so avoid converting things like numbers and dates into strings.
You won't need to worry about the peculiarities of date formatting.
You eliminate some security concerns.
#GordonLinoff all fields in db are all caps
If that is true then that is your problem. SQL usually performs case sensitive comparisons of character/string values unless you tell it not to do so, such as with STRCMP() (MySQL 4+), LOWER() or UPPER() (SQLServer, Firebird), etc. I would also go as far as wrapping the conditions in parenthesis as well:
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (STRCMP(CARD, "'+name+'") = 0) ORDER by ID';
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (LOWER(CARD) = "'+LowerCase(name)+'") ORDER by ID';
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (UPPER(CARD) = "'+UpperCase(name)+'") ORDER by ID';
This is or was an issue with the
With Adoquery2 do
begin
...
end
when using name in the sql, it was really getting adoquery2.name not the var name. I fixed this by changing name to Cname had no more issues after that.

SQL REPLACE function, how to replace single letter

My code is as follows:
REPLACE(REPLACE(cc.contype,'x','y'),'y','z') as ContractType,
This REPLACE's correctly what I would like, but it unfortunatley changes all "z's" to "y's" when I would like
x > y
y > z
Does this make sense? I would not like all of the new Y's to then change again in my second REPLACE function. In Microsoft Access, I would do this with the following
Iif(cc.contype = x, y, iif(cc.contype = y, x))
But I am not sure how to articulate this in SQL, would it be best I do this kind of thing in the client side language?
Many thanks.
EDIT: Have also tried with no luck:
CASE WHEN SUBSTRING(cc.contype, 1, 1) = 'C'
THEN REPLACE(cc.contype, 'C', 'Signed')
CASE WHEN SUBSTRING(cc.contype, 1, 1) = 'E'
THEN REPLACE(cc.contype, 'E', 'Estimate') as ContractType,
Try doing it the other way round if you don't want the new "y"'s to become "z"'s:
REPLACE(REPLACE(cc.contype,'y','z'),'x','y') as ContractType
Not that I'm a big fan of the performance killing process of handling sub-columns, but it appears to me you can do that just by reversing the order:
replace(replace(cc.contype,'y','z'),'x','y') as ContractType,
This will transmute all the y characters to z before transmuting the x characters to y.
If you're after a more general solution, you can do unioned queries like:
select 'Signed: ' || cc.contype as ContractType
wherecc.contype like 'C%' from wherever
union all select 'Estimate: ' || cc.contype as ContractType
where cc.contype like 'E%' from wherever
without having to mess about with substrings at all (at the slight cost of prefixing the string rather than modifying it, and adding any other required conditions as well, of course). This will usually be much more efficient than per-row functions.
Some DBMS' will actually run these sub-queries in parallel for efficiency.
Of course, the ideal solution is to change your schema so that you don't have to handle sub-columns. Separate the contype column into two, storing the first character into contype_first and contype_rest.
Then whenever you want the full contype:
select contype_first || contype_rest ...
For your present query, you could then use a lookup table:
lookup_table:
first char(1) primary key
description varchar(20)
containing:
first description
----- -----------
C Signed:
E Estimate:
and the query:
select lkp.description || cc.contype_rest
from lookup_table lkp, real_table cc
where lkp.first = cc.first ...
Both these queries are likely to be blazingly fast compared to one that does repeated string substitutions on each row.
Even if you can't replace the single column with two independent columns, you can at least create the two new ones and use an insert/update trigger to keep them in sync. This gives you the old way and a new improved way for accessing the contype information.
And while this technically violates 3NF, that's often acceptable for performance reasons, provided you understand and mitigate the risks (with the triggers).
How about
REPLACE(REPLACE(REPLACE(cc.contype,'x','ahhhgh'),'y','z'),'ahhhgh','y') as ContractType,
ahhhgh can be replaced with whatever you like.

Should I quote numbers in SQL?

I remember reading about quoting stuff when doing a SQL query and that when you quote something, it becomes a string. I also read that numbers should not be quoted. Now, I can't find that quotation and I need to refresh my memory to see if I should quote numbers.
You should not quote numbers if you want to treat them as numbers.
You're correct by remembering that it makes it a string.
SELECT 10 AS x
is perfectly legal and will return (in most database engines) a column of datatype int (or a variation thereof.)
If you do this:
SELECT '10' AS x
instead you'll get a textual data type. This might too be suitable in some cases, but you need to decide whether you want the result as text or as a number.
Here's an example, where quoting would produce inconsistent results (in MySQL):
select 1 < 1.0; // returns 0
select '1' < '1.0'; // returns 1
That's because the second comparison is performed using the current string collation rather than numerically.
It's better not to quote numbers, since that would just be an extra unnecessary step for the database to convert the string literal into a numeric value for comparison and could alter the meaning of comparisons.
This answer is for Microsoft SQL Server, in particular MSSQL 2008 R2.
In handwritten SQL I would never quote numbers (unless inserting a value into a varchar column where the string inserted just happens to be a number). But sometimes when generating SQL programmatically it can make life simpler to just quote everything. (This is in database maintenance scripts or library routines that work on any table, without knowing the column types in advance.)
I wondered whether doing so would impose a performance penalty. If I've used a quoted value in my SQL statement, the server must parse it as a string and then have to convert it to integer. But then, parsing SQL would involve converting strings to integers anyway. And the query parse time is usually only a small fraction of the total.
I ran some test statements looking like
insert into #t values (123, 123, 123), (123, 123, 123)
insert into #t values ('123', '123', '123'), ('123', '123', '123')
but with a larger number of columns in #t, a larger number of value tuples inserted in one go, and each statement repeated many times. I happened to use Perl for that:
$dbh = my_database_connection(); # using DBD::Sybase
$n = 20; # this many value tuples, and also repeated this many times
$v = "'123'";
# $v = 123; # uncomment this to insert without quoting
#cols = 'aa' .. 'zz';
#ds = map { "[$_] int not null" } #cols;
#vs = map { $v } #cols;
$" = ", ";
$dbh->do("create table #t (#ds)");
foreach (1 .. $n) {
$sql = 'insert into #t values ';
$sql .= "(#vs), " foreach 1 .. $n;
$sql =~ s/, \z//;
$dbh->do($sql);
}
but you can trivially write the same benchmark in any language. I ran this several times for the quoted case and for the unquoted, and observed no significant difference in speed. (On my setup a single run took about ten seconds; you can obviously change $n to make it faster or slower.)
Of course, the generated SQL is bigger if it has the redundant quote characters in it. It surely can't be any faster, but it does not appear to be noticeably slower.
Given this result, I will simplify my SQL-generation code to add single quotes around all values, without needing to know the data type of the column to be inserted into. (The code does still need to defend against SQL injection by making sure the input value doesn't itself contain the ' character, or otherwise quoting cleverly.)
I still don't recommend quoting numbers in SQL in the normal course of things, but if you find you have to do it, it doesn't appear to cause any harm. Similarly, in generated code I put [] around column names whether needed or not, but I consider that unnecessary cruft in handwritten SQL.
I don't know what you may have read, but don't quote numbers.
Obviously, don't forget to check to make sure any value you passed is really a number.
Well, at the risk of igniting a flame, may I respectfully disagree a little here that it is NEVER ok to use single quotes around a numeric value? It seems to me that it SOMETIMES makes sense to use single quotes around a numeric value. If col1 is an INT column, then (using vbscript as an example)
sql = "INSERT " & foo & " INTO col1 WHERE ID = 1"
and
sql = "INSERT '" & foo & "' INTO col1 WHERE ID = 1"
will BOTH, when sql is executed, insert any integer value of foo correctly. But what if you want 0 to be inserted when foo is not initialized? Using a quoted numeric expression like this prevents an error and handles the null case. Whether or not you think this is good practice, it is certainly true.
eh... no you shouldn't?
I assume you mean by quoting enclosing in ' like 'this'
INSERT INTO table (foo) VALUES (999); is perfectly legal as long as foo is an INT type collumn
INSERT INTO table (foo) VALUES('foo'); Inserts the string foo into the table. You can't do this on INT type tables of course.