num_rows in postgres always return 1 - sql

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 ...

Related

SQL: delete only if subquery (containing 0, 1, or more rows) equals scalar value

Description
I'm trying to do something like:
DELETE FROM ...
-- `<my_id>` is any given long scalar value
WHERE <my_id> = (SELECT id FROM ... WHERE ...);
That syntax is not entirely correct. If the select subquery contains more than one row, then we get an error like:
Scalar subquery contains more than one row
However, my intended purpose is indeed:
if the subquery returns exactly 1 value equal to the scalar value (<my_id>) ==> delete
else (0, or 2 or more values, or 1 non-equal value) ==> do not delete (ignore)
Question
I do not want neither IN nor EXISTS. I need rather something like "equals", which can compare a scalar value against possibly multi-valued rows.
What is the syntax in SQL for that?
Stack
In particular, I'm testing this with H2.
One method is:
DELETE FROM ...
WHERE <my_id> IN (SELECT id FROM ... WHERE ...) AND
(SELECT COUNT(*) FROM . . . WHERE . . .) = 1;
However, this is more simply written as:
DELETE FROM ...
WHERE <my_id> = (SELECT MAX(id)
FROM . . .
WHERE . .
HAVING COUNT(*) = 1
) ;
If the count is not 1, then the subquery returns nothing and there is no match (so nothing is deleted).

CDbCommand::createCommand() returns zero affected rows inside migration

This code works just fine (all database items updated as expected):
foreach($idMap as $menuId=>$pageId)
{
$sql = "UPDATE `menus_items` SET link = '/content/show?id=".$pageId."' WHERE id = ".$menuId."; ";
$affectedRows = Yii::app()->db->createCommand($sql)->execute();
echo $affectedRows." affected rows\n";
}
But it prints 0 affected rows for each executed query. Why?
The same effect is, when executing many rows affecting statements in one SQL query:
$sql = '';
foreach($idMap as $menuId=>$pageId)
{
$sql .= "UPDATE `menus_items` SET link = '/content/show?id=".$pageId."' WHERE id = ".$menuId."; ";
}
$affectedRows = Yii::app()->db->createCommand($sql)->execute();
echo $affectedRows." affected rows\n";
What am I missing? Docs says, that CDbCommand::execute should return number of rows affected by the execution. Does this feature work, when used inside migration?
CDbCommand::execute returns the row Count from underlying PDO interface, PDOstatement::rowCount only returns the row count of the last statement.
I had tested this within migration to be sure that migrate script is not running any other commands for cleanup etc, this is not the case, I am able to get correct row values from within and outside migration as well.
The most likely reason you are getting 0 as the value is because of the update command did not affect any rows ( i.e. the link values were already set to the correct values), UPDATE will return 0 if no change has occurred.
Perhaps you already run the migration on your test db and migrated down to test it few more times, however during the subsequent passes no update actually happened.
Note in the second scenario only the count of the last command ( a single row update will be shown even if update changes the table as PDOstatement::rowCount only returns the count for last statement executed.

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

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.

django using .extra() got error `only a single result allowed for a SELECT that is part of an expression`

I'm trying to use .extra() where the query return more than 1 result, like :
'SELECT "books_books"."*" FROM "books_books" WHERE "books_books"."owner_id" = %s' % request.user.id
I got an error : only a single result allowed for a SELECT that is part of an expression
Try it on dev-server using sqlite3. Anybody knows how to fix this? Or my query is wrong?
EDIT:
I'm using django-simple-ratings, my model like this :
class Thread(models.Model):
#
#
ratings = Ratings()
I want to display each Thread's ratings and whether a user already rated it or not. For 2 items, it will hit 6 times, 1 for the actual Thread and 2 for accessing the ratings. The query:
threads = Thread.ratings.order_by_rating().filter(section = section)\
.select_related('creator')\
.prefetch_related('replies')
threads = threads.extra(select = dict(myratings = "SELECT SUM('section_threadrating'.'score') AS 'agg' FROM 'section_threadrating' WHERE 'section_threadrating'.'content_object_id' = 'section_thread'.'id' ",)
Then i can print each Thread's ratings without hitting the db more. For the 2nd query, i add :
#continue from extra
blahblah.extra(select = dict(myratings = '#####code above####',
voter_id = "SELECT 'section_threadrating'.'user_id' FROM 'section_threadrating' WHERE ('section_threadrating'.'content_object_id' = 'section_thread'.'id' AND 'section_threadrating'.'user_id' = '3') "))
Hard-coded the user_id. Then when i use it on template like this :
{% ifequal threads.voter_id user.id %}
#the rest of the code
I got an error : only a single result allowed for a SELECT that is part of an expression
Let me know if it's not clear enough.
The problem is in the query. Generally, when you are writing subqueries, they must return only 1 result. So a subquery like the one voter_id:
select ..., (select sectio_threadrating.user_id from ...) as voter_id from ....
is invalid, because it can return more than one result. If you are sure it will always return one result, you can use the max() or min() aggregation function:
blahblah.extra(select = dict(myratings = '#####code above####',
voter_id = "SELECT max('section_threadrating'.'user_id') FROM 'section_threadrating' WHERE ('section_threadrating'.'content_object_id' = 'section_thread'.'id' AND 'section_threadrating'.'user_id' = '3') "))
This will make the subquery always return 1 result.
Removing that hard-code, what user_id are you expecting to retrieve here? Maybe you just can't reduce to 1 user using only SQL.

Yii Framework: How to get the num_rows?

As the official documentation does not say how to do a simply "num_rows" with their system, i need some help here: How to get the amount of rows in the result set ?
Assuming:
$connection=Yii::app()->db;
$command=$connection->createCommand($sql);
This will work for insert, update and delete:
$rowCount=$command->execute();
execute(): performs a non-query SQL statement, such as INSERT, UPDATE and DELETE. If successful, it returns the number of rows that are affected by the execution.
For select, you could do the following:
$dataReader=$command->query();
This generates the CDbDataReader instance and CDbDataReader provides a rowCount property
http://www.yiiframework.com/doc/api/1.1/CDbDataReader#rowCount-detail
$rowCount = $dataReader->rowCount;
About rowCount => Returns the number of rows in the result set. Note, most DBMS may not give a meaningful count. In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
ActiveRecord has count method which can be used.
$cntCriteria = new CDbCriteria();
$cntCriteria->condition = "categoryId = :categoryId";
$cntCriteria->params[':categoryId'] = $categoryRow->categoryId;
$articleCount = Article::model()->count($cntCriteria);
There is one more way to do this. When we execute a sql query it will return the result as array only. So we can able get the count of the rows using count() function like below.
$output=User::model()->findAllBySql("select * from user");//User is a model belongs to the user table
$count_val=count($output);//$count_val has the value of number of rows in the output.