sqlite select where...and vs. case...when - sql

I'm new to sqlite and have a populated database where I need to determine if any of the digital organisms in the database have a threshold level of fitness (fitness>=2) and are alive (live=1). I also need to exclude the indivID (the Primary Key) of the organism for whom I am trying to find a fit mate (indivID INTEGER PRIMARY KEY).
Below I tried the first line of code but it threw a SQL error:
[SQLITE_ERROR] SQL error or missing database (near "CASE": syntax
error).
However I know the error is in the SQL statment because other functions are successfully accessing the database.
SELECT indivID FROM population CASE fitness >=2 WHEN live = 1 AND indivID!=[specific individual] ELSE NULL END
I have tried this next line of code, which works but does not properly exclude the indivID of the specific individual:
SELECT [some column names] FROM [a database] WHERE fitness>=2 AND live=1 AND indivID!=[specific individual]
I have three questions:
1) Where are my errors in the above statements
2) What is the difference between using "case...when" and "where...and" statements
3) It is possible and even probable on these queries that there may not be a "live=1" individual with high enough fitness (above 2) to qualify for the select statement, so will the outcome of both of these queries with no viable individual (if correctly written) then be null?
Thanks in advance for your help!

According to your first query, you seem to misunderstand the usage of case when. It's like an if for a single value. It's usually used to get the required value based on some column values, e.g.
SELECT CASE WHEN col>0 THEN 1 ELSE 0 END FROM table
However it can also be used for some nifty tricks on the condition, e.g.
SELECT col FROM table WHERE CASE WHEN some_param = 1 THEN col2 = 1 ELSE true END
This statement retrieves column col for rows where col2 = 1 if some input parameter is 1 or for all rows if input parameter is something else than 1.
Using WHERE ... AND checks the condition, e.g.
SELECT col FROM table WHERE col>0 AND col2=0
In your case, you're trying to select a null if no matching values are found. I don't think it's the right way of dealing with it. You should attempt to select what you're looking for and then check whether you got any rows back. You would essentially get to something like this:
ResultSet rs = stmt.executeQuery("SELECT [some column names] FROM [a database] WHERE fitness>=2 AND live=1 AND indivID!=[specific individual]");
if(rs.first()) {
//You have some data - loop over the resultset and retrieve it
}
else {
//There are no matches to your query
}
If you only want one match, then you can simplify this to
ResultSet rs = stmt.executeQuery("SELECT [some column names] FROM [a database] WHERE fitness>=2 AND live=1 AND indivID!=[specific individual] LIMIT 1");
String name = rs.first() ? rs.getString(1) : null;
Note that I used index 1 for getString - but it may be whatever you need.
EDIT: If you're dealing with SQLite, then you have a single-direction-moveable resultset, therefore you need to change the above code slightly:
ResultSet rs = stmt.executeQuery("SELECT [some column names] FROM [a database] WHERE fitness>=2 AND live=1 AND indivID!=[specific individual]");
if(!rs.isBeforeFirst()) {
//You have some data - loop over the resultset and retrieve it
}
else {
//There are no matches to your query
}
And, correspondingly,
ResultSet rs = stmt.executeQuery("SELECT [some column names] FROM [a database] WHERE fitness>=2 AND live=1 AND indivID!=[specific individual] LIMIT 1");
String name = !rs.isBeforeFirst() ? null : rs.getString(1) : null;;
Note the change of rs.first() to !rs.isBeforeFirst() and the reversal of the condition.

Related

Snowflake ignores statement in where clause where I'm comparing timestamps

so I'm building a SCD type 2 in snowflake, but it ignores the where clause in which is comparision between "to_timestamp" and "expiry_date". Expiry_date is a variable that is set to '9999-08-17 07:31:29.901000000' (as infinity) and To_timestamp is a column in table. I want to query only the rows that have to_timestamp set to infinity (they are still active) but snowflake seems to ignore this part of where clause. Below is some of the code (it should update the rows that are expired - that means change their "to_timestamp" to current time. and it does but it does to rows with timestamps of all kind - it ignores last line)
SET EXPIRY_DATE_NTZ = '9999-08-17 07:31:29.901000000';
SET CURRENT_DATE_NTZ = TO_TIMESTAMP_NTZ(CURRENT_TIMESTAMP());
UPDATE CUSTOMER_TARGET CT
SET CT.TO_TIMESTAMP = $CURRENT_DATE_NTZ
FROM POC.SNOWFLAKE_POC.CUSTOMER_STAGE CS
WHERE CT.C_CUSTOMER_ID = CS.C_CUSTOMER_ID
AND (CT.C_FIRST_NAME <> CS.C_FIRST_NAME OR CT.C_LAST_NAME <> CS.C_LAST_NAME OR CT.C_BIRTH_YEAR
<> CS.C_BIRTH_YEAR OR CT.C_BIRTH_COUNTRY <> CS.C_BIRTH_COUNTRY OR CT.C_LAST_REVIEW_DATE<>CS.C_LAST_REVIEW_DATE)
AND CT.TO_TIMESTAMP = $EXPIRY_DATE_NTZ;
I have two of these update statements (one for updates and one for deletes) and a merge statement for inserts. And it ignores the comparision in every single one, updating the rows that have "to_timestamp" set to something like "2021-08-24 07:11:53.510000000". I've tried every combination possible (between ... and ..., >= ... <=, <=, >=, comparing in "case" statement of update,...) - nothing. What could be the cause/solution?
As we do not know the structure of CUSTOMER_TARGET I would suggest to explicitly set the data type of EXPIRY_DATE_NTZ variable to match the column data type:
SET EXPIRY_DATE_NTZ = '9999-08-17 07:31:29.901000000';
SELECT $EXPIRY_DATE_NTZ;
DESCRIBE RESULT LAST_QUERY_ID();
to:
-- TIMESTAMP_NTZ as an example
SET EXPIRY_DATE_NTZ = '9999-08-17 07:31:29.901000000'::TIMESTAMP_NTZ;
SELECT $EXPIRY_DATE_NTZ;
DESCRIBE RESULT LAST_QUERY_ID();
By doing that way there are no "implicit conversions" involved in the process.
Another advice is usage of IS DISTINCT FROM instead of <>. IS DISTINCT FROM is NULL safe, which is important if columns are defined as nullable.
UPDATE CUSTOMER_TARGET CT
SET CT.TO_TIMESTAMP = $CURRENT_DATE_NTZ
FROM POC.SNOWFLAKE_POC.CUSTOMER_STAGE CS
WHERE CT.C_CUSTOMER_ID = CS.C_CUSTOMER_ID
AND (CT.C_FIRST_NAME IS DISTINCT FROM CS.C_FIRST_NAME
OR CT.C_LAST_NAME IS DISTINCT FROM CS.C_LAST_NAME
OR CT.C_BIRTH_YEAR IS DISTINCT FROM CS.C_BIRTH_YEAR
OR CT.C_BIRTH_COUNTRY IS DISTINCT FROM CS.C_BIRTH_COUNTRY
OR CT.C_LAST_REVIEW_DATE IS DISTINCT FROM CS.C_LAST_REVIEW_DATE)
AND CT.TO_TIMESTAMP = $EXPIRY_DATE_NTZ;
Your SQL does not have any issues with the filters (ORs are surrounded by the brackets etc). I assume that you have checked the execution profile, and did not see your filter (CT.TO_TIMESTAMP = '9999-08-17 07:31:29.901000000'). In this case, all rows in the target table should have this value in the column TO_TIMESTAMP.
I highly recommend you check the data first. If you are running multiple UPDATE/MERGE commands, you may miss that the data has already updated with this value.

cfsavecontent display double apostrophe in SQL statement

I have several OR in my SQL statement so I want to save a chuck of it in a cfsavecontent. Here is that part:
<cfsavecontent variable="checkDepartment">
<cfif #wrkDept# EQ #dept[2][1]#>
Department = 'Health' AND
<cfelse>
Department = '#wrkDept#' AND
</cfif>
</cfsavecontent>
But the error I get on the page shows 2 sets of apostrophes around the word Health.
SQL
SELECT COUNT(*) AS numItems
FROM IT_PROJECTS
WHERE
Department = ''Health'' AND
status = 'Cancelled'
Can anyone help me to only get a single apostrophe? Thanks
So this answer seems a lot more complicated than it really is. And without knowing specifically what your query looks like (re:OR conditions), I'm not really sure how to structure it. It can be better. The goal should be to make one single trip to your SQL server with the query that makes the most sense for the data you're trying to get. I'm not sure what you are trying to do with cfsavecontent, but I don't think you need it.
The bulk of my example query (https://trycf.com/gist/4e1f46bfa84a6748aced0f9ee8221c6d/acf2016?theme=monokai) is setup. I chose to go with a cfscript format, because as Redtopia said, I also find it much easier to build a dynamic query in cfscript.
After initial setup, I basically just script out the variables I'll use in my final queryExecute().
// Base query.
qry = "SELECT count(*) AS theCount FROM IT_PROJECTS WHERE 1=1 " ;
// This is our dynamic filter that we build below.
qfilter = {} ;
// Query options.
opts = { "dbtype":"query" } ;
After we have our base, I build up the dynamic part of the query. This is the part that will likely change quite a bit depending on your current needs and setup.
For the first part, I basically replaced your cfif with a ternary evaluation. I'm not sure how your data plays into the evaluation of dept or where that array comes from. But from there I build a basic included statement of the query and set up the queryparam values for it. Then I add a second check that will pick a different set of values for the query (currently based on even/odd seconds). Again, I'm not sure of the intent of your query here, so I just made something dynamic.
//////////// BUILD DYNAMIC FILTER ////////////
qdept = ( wrkDept == dept[2][1] ) ? 'Health' : wrkDept ;
/// This one is an included filter:
qry &= " AND department = :dpt AND status = :sts " ;
qfilter.dpt = {"value":qdept,"cfsqltype":"CFSQLVARCHAR"} ;
qfilter.sts = {"value":"Cancelled","cfsqltype":"CFSQLVARCHAR"} ;
/// Adding Dynamic ORs
// Dynamically set status based on even/odd seconds.
qStatus = ( now().second()%2==0) ? "Cancelled" : "Active" ;
qry &= " OR ( department = :dpt2 AND status = :sts2 ) " ;
qfilter.dpt2 = {value:"IT",cfsqltype:"CFSQLVARCHAR"} ;
qfilter.sts2 = {value:qStatus,cfsqltype:"CFSQLVARCHAR"} ;
This gives us a SQL string that looks like:
SELECT count(*) AS theCount
FROM IT_PROJECTS
WHERE 1=1
AND department = :dpt AND status = :sts
OR
( department = :dpt2 AND status = :sts2 )
With a SQL statement, the placement of AND and OR conditions can greatly impact the results. Use parenthesis to group conditions how you need them.
After we've built the query string, we just have to plug it and our queryparams into the queryExecute().
result = queryExecute( qry , qfilter , opts ) ;
And if we want to output our data, we can go:
writeOutput("There are " & result.theCount & " records." ) ;
Which gives us:
There are 8 records.
Again, I don't know what your main conditions look like. If you can give me an example of a query with a bunch of ORs and ANDs, I'll try to modify this for you.

Linq Query in VB

Good Day,
I am querying my database using Linq and I have run into a problem, the query searched a column for a search phrase and based on if the column has the phrase, it then returns the results, The query is below,
Dim pdb = New ProductDataContext()
Dim query =
From a In pdb.tblUSSeries
Join b In pdb.tblSizes_ On a.Series Equals b.Series
Where
a.Series.ToString().Equals(searchString) Or
b.Description.Contains(searchString) Or Not b.Description.Contains(Nothing)
Order By b.Series, b.OrderCode Ascending
Select New CustomSearch With
{
.Series = a.Series,
.SeriesDescription= a.Description,
.Coolant = a.Coolant,
.Material = a.Material,
.Standard = a.Standard,
.Surface = a.Surface,
.Type = a.Type,
.PointAngle = a.PointAngle,
.DiaRange = a.DiaRange,
.Shank = b.Shank,
.Flutes = b.Flutes,
.EDPNum = b.EDPNum,
.SizesDescription = b.Description,
.OrderCode = b.OrderCode
}
Return query
I think the problem is that, in the table certain rows are NULL, so when it is checking the column for the phrase and it encounters a row that is null it, breaks and returns this error,
The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
I have ran this query against another column that has all the rows populated with data and it returns the results ok.
So my question is how can I write it in VB to query the db with the supplied searchstring and return the results, when some of the rows in the columns have null values.
Any help would be great.
The exception occurs when you make the projection (i.e. select new CustomSearch)
And yes your trying to assign Null to some int property
(Not sure which one of your properties that is)
one of 2 choices :
1) Use nullalbe types for your properties (or just that one property).
2) project with an inline If ( ?? in C#) , I don't know VB so don't catch me on the syntax.
Taking Series just as an example i don't know if it's an int or if that's the problematic property
Select New CustomSearch With
{
.Series = If(a.Series Is Nothing,0, CInt(a.Series))
}
In C#
Select new CustomSearch
{
Series = a.Series ?? 0;
}

num_rows in postgres always return 1

I'm trying to do a SELECT COUNT(*) with Postgres.
What I need: Catch the rows affected by the query. It's a school system. If the student is not registered, do something (if).
What I tried:
$query = pg_query("SELECT COUNT(*) FROM inscritossimulado
WHERE codigo_da_escola = '".$CodEscola."'
AND codigo_do_simulado = '".$simulado."'
AND codigo_do_aluno = '".$aluno."'");
if(pg_num_rows($query) == 0)
{
echo "Error you're not registered!";
}
else
{
echo "Hello!";
}
Note: The student in question IS NOT REGISTERED, but the result is always 1 and not 0.
For some reason, when I "show" the query, the result is: "Resource id #21". But, I look many times in the table, and the user is not there.
You are counting the number of rows in the answer, and your query always returns a single line.
Your query says: return one row giving the number of students matching my criteria. If no one matches, you will get back one row with the value 0. If you have 7 people matching, you will get back one row with the value 7.
If you change your query to select * from ... you will get the right answer from pg_num_rows().
Actually, don't count at all. You don't need the count. Just check for existence, which is proven if a single row qualifies:
$query = pg_query(
'SELECT 1
FROM inscritossimulado
WHERE codigo_da_escola = $$' . $CodEscola . '$$
AND codigo_do_simulado = $$' . $simulado. '$$
AND codigo_do_aluno = $$' . $aluno . '$$
LIMIT 1');
Returns 1 row if found, else no row.
Using dollar-quoting in the SQL code, so we can use the safer and faster single quotes in PHP (I presume).
The problem with the aggregate function count() (besides being more expensive) is that it always returns a row - with the value 0 if no rows qualify.
But this still stinks. Don't use string concatenation, which is an open invitation for SQL injection. Rather use prepared statements ... Check out PDO ...

SQL regex and field

I want to change the query to return multiply values in extra_fields, how can I change the regex? Also I don't understand what extra_fields is - is it a field? If so why it is not called with the table prefix like i.extra_fields?
SELECT i.*,
CASE WHEN i.modified = 0 THEN i.created ELSE i.modified END AS lastChanged,
c.name AS categoryname,
c.id AS categoryid,
c.alias AS categoryalias,
c.params AS categoryparams
FROM #__k2_items AS i
LEFT JOIN #__k2_categories AS c ON c.id = i.catid
WHERE i.published = 1
AND i.access IN(1,1)
AND i.trash = 0
AND c.published = 1
AND c.access IN(1,1)
AND c.trash = 0
AND (i.publish_up = '0000-00-00 00:00:00'
OR i.publish_up <= '2013-06-12 22:45:19'
)
AND (i.publish_down = '0000-00-00 00:00:00'
OR i.publish_down >= '2013-06-12 22:45:19'
)
AND extra_fields REGEXP BINARY '(.*{"id":"2","value":\["[^\"]*1[^\"]*","[^\"]*2[^\"]*","[^\"]*3[^\"]*"\]}.*)'
ORDER BY i.id DESC
The extra_fields is a column of the #__k2_items table. The table qualifier can be omitted, because it is not ambiguous in this query. The column is JSON encoded. That is a serialization format used to store information which is not searchable by design. Applying a RegExp may work one day, but fail another day, since there is no guarantee for id preceeding value (as in your example).
The right way
The right way to filter this is to ignore the extra_fields condition in the SQL query an evaluate in the resultset instead. Example:
$rows = $db->loadObjectList('id');
foreach ($rows as $id => $row) {
$extra_fields = json_decode($row->extra_fields);
if ($extra_fields->id != 2) {
unset($rows[$id]);
}
}
The short way
If you can't change the database layout (which is true for extensions you want to keep updateable), you must split the condition into two, because there is no guarantee for a certain order of the subfields. For some reason, one day value may occur before id. So change your query to
...
AND extra_fields LIKE '%"id":"2"%'
AND extra_fields REGEXP BINARY '"value":\[("[^\"]*[123][^\"]*",?)+\]'
Prepare an intermediate table to hold the contents of extra_fields. Each extra_fields field will be converted into a series of records. Then do a join.
Create a trigger and cronjob to keep the temp table in sync.
Another way is to write UDF in Perl that will decode the field, but AFAIK it is not indexable in mysql.
Using an external search engine is out of scope.
Ok, i didnt want to change the db strucure, i gost some help and changed the regex intoAND extra_fields REGEXP BINARY '(.*{"id":"2","value":\[("[^\"]*[123][^\"]*",?)+\]}.*)'
and i got the right resaults
Thanks