Selecting a date range where date is not null with Propel - symfony1

Using Propel I would like to find records which have a date field which is not null and also between a specific range.
N.B. Unfortunately, as this is part of a larger query, I cannot utilise a custom SQL query here.
For example: I may have records like this:
---------------------
| ID | DUE_DATE |
---------------------
| 1 | NULL |
| 2 | 01/01/2010 |
| 3 | 02/01/2010 |
| 4 | NULL |
| 5 | 05/01/2010 |
---------------------
I may want to return all the rows with a due_date between 01/01/2010 and 02/01/2010 but I don't want to return those records where due_date is NULL.
In the example I only want to return rows 2 and 3.
However, Propel seems to overwrite my NOTNULL criteria.
Is it possible to do this with Propel?
Thanks!

Why do you create the separate Criterion objects?
$start_date = mktime(0, 0, 0, date("m") , date("d")+$start, date("Y"));
$end_date = mktime(0, 0, 0, date("m") , date("d")+$end, date("Y"));
$c = new Criteria();
$c->add(TaskPeer::DUE_DATE, $end_date, Criteria::LESS_EQUAL);
$c->addAnd(TaskPeer::DUE_DATE, $start_date, Criteria::GREATER_EQUAL);
$c->addAnd(TaskPeer::DUE_DATE, null, Criteria::ISNOTNULL);
When I try this in Propel 1.2, 1.3 or 1.4, I get the following SQL statement:
SELECT task.TASK_ID, task.DUE_DATE FROM task WHERE ((task.DUE_DATE<=:p1 AND task.DUE_DATE>=:p2) AND task.DUE_DATE IS NOT NULL )
The $c->add() method replaces the current criterion for the given field. You create your Criterions for TaskPeer::DUE_DATE, so they will always replace the previous ones.

I did't get remove the null entries section, I think it will produce: tasks.due_date IS NULL AND tasks.due_date IS NULL.
Anyway, maybe you can use Criteria::CUSTOM to write raw-SQL WHERE clause? Example from Propel documentation:
$con = Propel::getConnection(ReviewPeer::DATABASE_NAME);
$c = new Criteria();
$c->add(ReviewPeer::REVIEW_DATE, 'to_date('.ReviewPeer::REVIEW_DATE.', \'YYYY-MM-DD\') = '.$con->quote($date->format('Y-m-d'), Criteria::CUSTOM);

Related

Hive DB - struct datatype join - with different structure elements

I am pretty new in work with Hive DB and struct data types. I used only basic SELECT statements until now.
I need to join two tables to combine them in my SELECT statement.
Tables have struct datatype with same name, but with different elements inside. This is how tables look like:
TABLE 1
table_one(
eventid string,
new struct<color:string, size:string, weight:string, number:string, price:string>,
date string
)
11 | {"color":"yellow", "size":"xl", "weight":"10", "number":"1111", "price":"1"} | 08-21-2004
12 | {"color":"yellow", "size":"xxl", "weight":"12", "number":"2111", "price":"2"} | 08-21-2004
TABLE 2
table_two(
eventid string,
new struct<number:string, price:string>,
date string,
person string)
11 | {"number":"31", "price":"1"} | 08-21-2004 | john
12 | {"number":"32", "price":"2"} | 08-21-2004 | joe
With SELECT query I need to get value of element 'color' from table_one, but instead that, I am getting value of element 'number' from table_two, query is following:
select
s.eventid,
v.date,
s.new.color,
s.new.size
from table_one s join table_two v where s.eventid = v.eventid;
With s.new.color - instead getting for example value 'yellow' from table_one, I am getting value '31' from table_two. How I am supposed to get wanted value from table_one?
Expected result:
11 | 08-21-2004 | yellow | xl
But I got:
11 | 08-21-2004 | 31 | 1
So how can I select proper value from struct datatype from desired table?
(Please have on mind that this is just simplified example of my problem, I didn't provide exact code or structures of tables to make this clearer for one who will try to provide me answer. I need to use join because I need proper values for some column from table_two)

How to return distinct rows in rails?

I have a model Program containing fields program_title, department_id and date. I have inserted two rows having same program title and date but different department_id.
Insert into programs(program_title,date,department_id) Values ("prog1","4/2/2017","1");
Insert into programs(program_title,date,department_id) Values ("prog1","4/2/2017","2");
Now I want to return rows which will be distinct by program_title whatever be the department_id. I have tried,
#event_contents=Program.select(:id,:date,:program_title).distinct(:program_title)
But still it returns both the rows. Any help is appreciated.
SQL can only collapse rows where all values are the same when using DISTINCT. Because you are selecting id, which is different for every record, the rows are not distinct. E.g.:
---------------------------------
| id | program_title | date |
---------------------------------
| 1 | prog1 | 4/2/2017 |
| 2 | prog1 | 4/2/2017 |
---------------------------------
You'll need to exclude the id from your #select for it to work:
Program.select(:date, :program_title).distinct
Try this
Program.all.distinct(:program_title).pluck( :id,:program_title,:date)
It will return data as array of elements though
Hope it helps

How to make GORM create an index on Map?

In my Grails 2.3.7 project, I have a Product domain class, like this:
class Product {
String code
String description
Map attributes
static constraints = {
code(unique: true)
}
static mapping = {
code index: 'Code_Idx'
attributes fetch: 'join'
cache true
id generator: 'hilo'
}
}
It translates into this database:
create table product (id bigint not null, version bigint not null, code varchar(255) not null unique, description varchar(255) not null, primary key (id));
create table product_attributes (attributes bigint, attributes_idx varchar(255), attributes_elt varchar(255) not null);
create index Code_Idx on product (code);
With some 4000 products in database, the scaffold listing shows them just fine.
Except, when I click "sort" on code - because there is no index - so my server does this:
explain select this_.id as id14_0_, this_.version as version14_0_, this_.code as code14_0_, this_.description as descript4_14_0_,
attributes2_.attributes as attributes14_2_, attributes2_.attributes_elt as attributes3_2_, attributes2_.attributes_idx as attributes2_2_
from product this_ left outer join product_attributes attributes2_
on this_.id=attributes2_.attributes
order by lower(this_.code) desc limit 90, 100
+----+-------------+--------------+------+---------------+------+---------+------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+---------------+------+---------+------+-------+---------------------------------+
| 1 | SIMPLE | this_ | ALL | NULL | NULL | NULL | NULL | 4086 | Using temporary; Using filesort |
| 1 | SIMPLE | attributes2_ | ALL | NULL | NULL | NULL | NULL | 43975 | |
+----+-------------+--------------+------+---------------+------+---------+------+-------+---------------------------------+
Obviously, this takes ages. I can manually add the index:
ALTER TABLE `product_attributes` ADD INDEX(`attributes`);
and then it works OK. I think it should have been created automatically in the first place - there is very little sense in this schema without the index - but OK, I can ping Gorm to do it. My question is - what can I put into the domain class to have Gorm add this index ?
Grails doesn't automatically create indexes on such columns as 'attributes' in your example. In order to create and manage these indexes I highly recommend using the Database Migration Plugin. The documentation is well written, and outlines how to use it.

SelfJoin using Symfony 1.4/propel 1.4

I need to do self join using Symfony 1.4/Propel 1.4. My tables/db are too big to put here but an example table is given below to replicate the issue I'm facing.
Consider following example table with example data
Table Employee
----------------------------------------
|id | name | mid |
----------------------------------------
|1 | CEO |NULL |
|2 | CTO |1 |
|3 | CFO |1 |
|4 | PM1 |2 |
|5 | TL1 |4 |
----------------------------------------
Here first column is employee, second is employee name and 3rd is manager id. mid is link to another row in same table. For example, CTO(2) reports to CEO(1) so mid in second row is 1.
I need following output:
---------------------
|ename | manager |
---------------------
|CTO | CEO |
|CFO | CEO |
|PM1 | CTO |
|TL1 | PM1 |
---------------------
The SQL query will be:
SELECT e.name,m.name
FROM employee e, employee m
WHERE e.mid=m.id
AND e.mid NOT NULL;
My problem is, how do I write same query in Symfony/Propel 1.4? I try following
$c = new Criteria();
$c->clearSelectColumns();
$c->addSelectColumn(EmployeePeer::NAME.' as ename');
$c->addSelectColumn(EmployeePeer::NAME.' as manager');
$c->setPrimaryTableName(EmployeePeer::TABLE_NAME);
$c->addJoin(EmployeePeer::MID, EmployeePeer::ID, Criteria::INNER_JOIN);
$c->add(EmployeePeer::MID, NULL, Criteria::EQUAL);
Even I know this query do not make any sense and as per my expectation, I got PropelException.
But self join is one of the common database operation and I'm sure Propel must support that. Can someone please tell how to achieve above requirements in Symfony/Propel 1.4
According to this SQLFiddle, the SQL you want to perform is:
SELECT e.name as ename, m.name as manager
FROM employee e
LEFT JOIN employee m ON e.mid = m.id WHERE e.mid IS NOT NULL;
Like YouthPark, I think addAlias is the solution and I will do something like that:
$c = new Criteria();
$c->clearSelectColumns();
$c->addSelectColumn(EmployeePeer::NAME.' as ename');
$c->addSelectColumn(EmployeePeer::NAME.' as manager');
$c->addAlias('c2', EmployeePeer::TABLE_NAME);
$c->addJoin(EmployeePeer::ID, EmployeePeer::alias('c2', EmployeePeer::MID), Criteria::LEFT_JOIN);
$c->add(EmployeePeer::MID, Criteria::ISNOTNULL);
I'm not sure about the addSelectColumn part, by the way.
Well I never tried so not sure if that help you or not but there is no other answers so you might try/further search addAlias method, if you are stuck.
$notifCrit->addAlias("A", ThreadsPeer::TABLE_NAME);
$notifCrit->add("A.father_id", ThreadsPeer::FATHER_ID."=A.father_id", Criteria::CUSTOM);
Taken from last comment of old symfony forums
Not sure but Propel 1.4 might not support self join with build in methods as it need to set alias. So you need custom query as in above example.
$c = new Criteria();
$c->addJoin(ArticlePeer::AUTHOR_ID, AuthorPeer::ID);
$c->add(AuthorPeer::NAME, 'John Doe');
$articles = ArticlePeer::doSelect($c);

Override Rails updated_at attribute

I would like to modify updated_at attribute so that every time record is updated it would only show date and hours while hours and minutes being zeros. Instead of 2010-08-13 11:04:12, there would be 2010-08-13 11:00:00. What is the most rational way to do this in Rails 2.3?
Update
The reason why I want to do what I have asked is because I would like to sort data, by date with hours and seconds omitted.
Thanks!
Don't try to change the value stored in the database; change how you are outputting it:
timestamp = #model.updated_at # where #model is your model object
# output it in a format that overrides the values for minute and second
puts timestamp.strftime "%Y-%m-%d %H:00:00"
See the strftime ruby doc for other format specifiers you can use.
Based on your update, I would still not change how updated_at is stored, but instead format the date within the ORDER BY of your query. For instance, you could
ORDER BY DATE(updated_at), HOUR(updated_at), ...
I did a very quick profiling test on a live table in my application, which has about 22,000 records. ORDER BY updated_at took between 2.94 and 3.19s to complete, with a mean time of 3.00s. ORDER BY DATE(updated_at), HOUR(updated_at) took between 2.93 and 3.06s to complete, with a mean time of 2.99s. Here's the profiling data:
+----------+------------+-----------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------------------------------------+
| 1 | 2.94530500 | SELECT * FROM posts ORDER BY updated_at |
| 2 | 2.94583800 | SELECT * FROM posts ORDER BY updated_at |
| 3 | 3.18711700 | SELECT * FROM posts ORDER BY updated_at |
| 4 | 2.96923700 | SELECT * FROM posts ORDER BY updated_at |
| 5 | 2.97255400 | SELECT * FROM posts ORDER BY updated_at |
| 6 | 3.06706800 | SELECT * FROM posts ORDER BY DATE(updated_at), HOUR(updated_at) |
| 7 | 3.00414400 | SELECT * FROM posts ORDER BY DATE(updated_at), HOUR(updated_at) |
| 8 | 2.95551500 | SELECT * FROM posts ORDER BY DATE(updated_at), HOUR(updated_at) |
| 9 | 3.02181900 | SELECT * FROM posts ORDER BY DATE(updated_at), HOUR(updated_at) |
| 10 | 2.93130000 | SELECT * FROM posts ORDER BY DATE(updated_at), HOUR(updated_at) |
+----------+------------+-----------------------------------------------------------------+
It's a time stamp object so your probably best off just to use a helper function to truncate the min/sec info.
whatever.created_at.strftime("%Y-%m-%d %H:00:00")
I would recommend Daniel's solution. However, if you're 100% positive that you want to save the record with your modified "updated_at" field, you'll have to re-implement rails' auto-timestamping for the given model.
So, in your model, you can do the following:
self.record_timestamps = false # disables rails' auto-timestamping
before_create :set_created_at
before_save :set_updated_at
def set_created_at
self.created_at = Time.now
end
def set_updated_at
self.updated_at = Time.now.change({:min => 0, :sec => 0}) # clears minutes and seconds
end
This isn't something you should do with active record. It's something that a view helper could accomplish and you don't need to alter the way things are getting saved which will probably just be more work in the future.
Think about strftime and how you can alter that instead.
In a helper file:
def format_date_for_rounded_hour(date)
date.striftime("%Y-%m-%d %H:00")
end

Resources