I'm trying to group a query's response by day, month, or year based on user input.
To do this I'm extracting the day, month, or year from the log_time column AS when and then trying to GROUP BY when but CakePHP says:
SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "when" LINE 1: ...2-31' GROUP BY "Passenger"."route_id", "Route"."name", when ^
Here's an example of my function call where the grouping is 'month'.
$fields = array(
"CONCAT(DATE_PART('year', \"Passenger\".\"log_time\"), '-', DATE_PART('month', \"Passenger\".\"log_time\")) AS when",
"Passenger.route_id",
"Route.name",
"SUM(Passenger.embarked) AS embarked"
);
$conditions = array(
"Passenger.log_time >=" => $start,
"Passenger.log_time <=" => $end
);
$group = array("Passenger.route_id", "Route.name", "\"when\"");
return $this->find('all', array('fields' => $fields, 'conditions' => $conditions, 'group' => $group));
This is using Postgres on Cake 2.3.5. Has anyone else figured out how to do this?
when is a keyword that is used with CASE. Looks like PostgreSQL can figure out that the when in your select is just an identifier but there isn't enough context to figure it out in the GROUP BY. I'd quote it in both places:
"CONCAT(DATE_PART('year', \"Passenger\".\"log_time\"), '-', DATE_PART('month', \"Passenger\".\"log_time\")) AS \"when\"",
and:
$group = array("\"when\"", "Passenger.route_id", "Route.name");
Related
I'm getting a syntax error when trying to use the GROUPING SETS function in my Postgres DB.
I've looked at the documentation here and I believe the syntax is correct, but I'm still getting an error.
I believe the syntax should be GROUP BY GROUPING SETS ((COLUMN), (COLUMN), ()) to get the grand total of my SUM() from the the SELECT part of my code. So I put in GROUP BY GROUPING SETS ((amount.date), ( places.place), ());
What am I doing wrong here?
SELECT
EXTRACT(MONTH FROM amount.date),
places.place,
CASE
WHEN places.place = 'A' THEN SUM(amount.amount)
WHEN places.place = 'B' THEN SUM(amount.amount)
WHEN places.place = 'C' THEN SUM(amount.amount)
ELSE SUM((amount.amount) * 2.5)
END AS "Total"
FROM amount
LEFT JOIN places ON places.id = amount.id
WHERE EXTRACT(YEAR FROM amount.date) = 2017
GROUP BY GROUPING SETS ((amount.date), (places.place), ());
I'm trying to make a request and I have an error when I'm using LIKE on the created_at field.
I'm using Laravel 5.3 with Eloquent (ORM).
$date = Carbon::now();
foreach ($hours as $hour) {
$chart[$hour]['hour'] = $hour;
$chart[$hour]['allowed'] = VisitsAllowed::where('created_at', 'LIKE', Carbon::parse($date)->format('Y-m-d %'))->count();
$chart[$hour]['denied'] = VisitsDenied::where('created_at', 'LIKE', Carbon::parse($date)->format('Y-m-d %'))->count();
}
Error :
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist: timestamp without time zone ~~* unknown
LINE 1: ... aggregate from "visits_allowed" where "created_at" LIKE $1
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. (SQL: select count(*) as aggregate from "visits_allowed" where "created_at" LIKE 2017-05-30 %)
Can someone help me to find a solution.
Please try this instead :
foreach ($hours as $hour) {
$chart[$hour]['hour'] = $hour;
$chart[$hour]['allowed'] = VisitsAllowed::where('created_at', '=', Carbon::parse($date)->format('Y-m-d'))->count();
$chart[$hour]['denied'] = VisitsDenied::where('created_at', '=', Carbon::parse($date)->format('Y-m-d'))->count();
}
Not the prettiest solution but testing if the date (say 2017-05-30) is between: yyyy-mm-dd 00:00:00 and yyyy-mm-dd 23:59:59 works.
$chart[$hour]['allowed'] = VisitsAllowed::where('created_at', '>=', Carbon::parse($date )->format('Y-m-d') . ' 00:00:00')
->where('created_at', '<=', Carbon::parse($date )->format('Y-m-d') . ' 23:59:59')
->count();
$chart[$hour]['denied'] = VisitsDenied::where('created_at', '>=', Carbon::parse($date )->format('Y-m-d') . ' 00:00:00')
->where('created_at', '<=', Carbon::parse($date )->format('Y-m-d') . ' 23:59:59')
->count();
I've sql query
select * from table1
left join (values (4),(1800),(103500)) AS "filter (id) on table1.id=filter.id
By default Zend_Db_Select table quoted.
For example:
$result = '(values (4),(1800),(103500)) AS filter (id)';
$select->joinInner($result, "table1.id = filter.id", '');
result:
SELECT * FROM "table1"
INNER JOIN "(values (4),(1800),(103500)) filter (id)" ON table1.id=filter.id
Me need
SELECT * FROM "table1"
INNER JOIN (values (4),(1800),(103500)) filter (id) ON table1.id=filter.id
How can disable quote table?
Try adding $result to your $select as a Zend_Db_Expr.
This is a little tricky. Look at the code below.
$dbh = Zend_Db_Table::getDefaultAdapter();
$select = $dbh->select();
$select->from('table1');
$select->joinInner(
array('filter (id)' => new Zend_Db_Expr('(values (4),(1800),(103500))')),
"table1.id = filter.id",
array()
);
echo $select->assemble() . PHP_EOL;
This code by default outputs the following statement which is not what we really want because identifier filter (id) is quoted. Here is the output.
SELECT `table1`.* FROM `table1`
INNER JOIN (values (4),(1800),(103500)) AS `filter (id)` ON table1.id = filter.id
We need to disable autoQuoteIdentifiers in configuration options. For example:
'db' => array(
'adapter' => 'pdo_mysql',
'isDefaultTableAdapter' => true,
'params' => array(
'host' => '<host>',
'username' => '<user>',
'password' => '<pass>',
'dbname' => '<db>',
'options' => array(
'autoQuoteIdentifiers' => false,
),
),
)
We get the following output
SELECT table1.* FROM table1
INNER JOIN (values (4),(1800),(103500)) AS filter (id) ON table1.id = filter.id
Note that in this case developer is responsible for quoting the identifiers when needed.
I think it's impossible to selectively disable quoting for one of the table alias. Well at least I found this impossible when reviewed 1.x Zend Framework code I have here locally ;)
I'm writing a web app in Perl using Dancer framework. The database is in sqlite and I use DBI for database interaction.
I'm fine with select statements, but I wonder is there a way to count selected rows.
E.g. I have
get '/' => sub {
my $content = database->prepare(sprintf("SELECT * FROM content LIMIT %d",
$CONTNUM));
$content->execute;
print(Dumper($content->fetchall_arrayref));
};
How do I count all items in the result without issuing another query?
What I want to achieve this way is showing 30 items per page and knowing how many pages there would be. Of course I can run SELECT COUNT (*) foo bar, but it looks wrong and redundant to me. I'm looking for a more or less general, DRY and not too heavy on database way to do so.
Any SQL or Perl hack or a hint what should I read about would be appreciated.
// I know using string concatenation for querys is bad
You have to do it the hard way: one query to get the count and another to get your desired slice of the row set:
my $count = $database->prepare('SELECT COUNT(*) FROM content');
$count->execute();
my $n = $count->fetchall_arrayref()->[0][0];
my $content = $database->prepare('SELECT * FROM content LIMIT ?');
$content->execute($CONTNUM);
#...
Not too familiar with perl, but I assume you can just store the result of $content->fetchall_arrayref and retrieve the count from that array befor you print it.
[edit]
Something like
my $ref = $content->fetchall_arrayref;
my $count = scalar(#$ref);
Don't use sqlite myself but the following might work:
select * from table join (select count(*) from table);
Whether the above works or not the first thing I'd look for is scrollable cursors if you are going to page through results - I doubt sqlite has those. However, in DBI you can use fetchall_arrayref with a max_rows to fetch a "page" at a time. Just look up the example in the DBI docs under fetchall_arrayref - it is something like this:
my $rowcache = [];
while( my $row = ( shift(#$rowcache) || shift(#{$rowcache=$sth->fetchall_arrayref(undef,100)||[]}) )
) {
# do something here
}
UPDATE: Added what you'd get with selectall_hashref assuming the table is called content with one integer column called "a":
$ perl -le 'use DBI; my $h = DBI->connect("dbi:SQLite:dbname=fred.db"); my $r = $h->selectall_hashref(q/select * from content join (select count(*) as count from content)/, "a");use Data::Dumper;print Dumper($r);'
$VAR1 = {
'1' => {
'count' => '3',
'a' => '1'
},
'3' => {
'count' => '3',
'a' => '3'
},
'2' => {
'count' => '3',
'a' => '2'
}
};
If you want to know how many results there will be, as well as getting the results themselves, all in one query, then get the count as a new value:
SELECT COUNT(*) AS num_rows, * from Table WHERE ...
Now the row count will be the first column of every row of your resultset, so simply pop that off before presenting the data.
I'm stuck with the following problem. In my product table i've two columns
Date_start and Date_end (both of them are a DATE datatype in my table).
I want to check if the current date is between the Date_start and Date_end then the status must be 'not available', else it must have the status 'available'.
How can I fix that in Zend_Db_Expr?
I've now the following query.
$getProducts = $this->oSelect
->from(array('p'=>'producten'))
->columns(array('link' => "CONCAT('/t/', p.titel_key)"))
->joinLeft(array('c'=>'categorie'),'p.categorie_id = c.id',array('cat_titel'=>'c.titel'))
->joinLeft(array('sc'=>'subcategorie'),'p.subcategorie_id = sc.id', array('subcat_titel'=>'sc.titel'))
->where('p.online = 1');
In your columns:
->columns(array('link' => "CONCAT('/t/', p.titel_key)",
'status' => new Zend_Db_Expr("...")))
The 'CASE' should look something like this (i split it out for readability);
CASE WHEN p.Date_end < NOW() AND p.Date_start > NOW()
THEN 'not available' ELSE 'available' END
Zend_Db_Expr will take anything you give it and use it literally. Just remember that any DB specific commands might break if for some reason you switch systems.