In PostgreSQL I have a table with a column of the timestamp type. I hope this maps to a Kotlin Exposed data type of datetime (docs are unclear, but that's another question).
My question now is: how do I update/insert a value with the SQL literal NOW(), so that the timestamp is set to the current timestamp by the database server?
I'm currently trying to apply this in the Exposed DSL API like this:
object MyTable : Table() {
val col1: Column<String> = varchar("col1", 100).primaryKey()
val last_update: Column<DateTime?> = datetime("last_update").nullable()
}
MyTable.update ({ MyTable.col1 eq "abcd1234" }) {
// How do I put a literal 'NOW()' here?
it[MyTable.last_update] = ...
}
I found a way to declare a custom Expression in the Exposed source code, like this:
val nowExpression = object : Expression<DateTime>() {
override fun toSQL(queryBuilder: QueryBuilder) = "NOW()"
}
But how do I apply that in my update/insert statement?
Kotlin unit tests I found:
Use Kotlin-side of DateTime.now() (not what I want), or
Use a column default value of now, then omitting the column value in the insert. This is where I found the literal NOW() expression. However, I want to keep my default to NULL on that column.
I did not find a unit test to apply a custom expression in a DSL insert/update, but perhaps I am missing something.
There is update function on Insert/Update statements which accepts Expression.
MyTable.update ({ MyTable.col1 eq "abcd1234" }) {
it.update(MyTable.last_update, nowExpression)
}
Related
I am trying to build dynamic WHERE clause with Dapper SQL builder. This works very well for all cases except BETWEEN. Have tried the below approach with BETWEEN (just like IN clause)
SqlBuilder sqlBuilder = new SqlBuilder();
var sqlTemplate = sqlBuilder.AddTemplate(#"Select * from tbl_example /**Where**/");
sqlBuilder.Where(#"Col BETWEEN #colparam", new { paramValue });
con.Query(sqlTemplate.RawSql, sqlTemplate.Parameters);
When using SQL Profiler, I observed that the query generated from dapper looks like
Select * from tbl_example Where Col BETWEEN (#colparam1,#colparam2)
NOTE:
paramValue is an array of Integer which i receive via API
I do understand that this can be written like Col BETWEEN #colparam1 AND #colparam2 or replacing BETWEEN with < and >
As you know, BETWEEN expects two parameters (#colparam1 AND #colparam2), but you're passing only a single one (#colparam).
Assuming that your array contains exactly the boundaries (min and max), try this:
sqlBuilder.Where(#"Col BETWEEN #val1 AND #val2", new { val1 = paramValue[0], val2 = paramValue[1] });
I have model I created on the fly for peewee. Something like this:
class TestTable(PeeweeBaseModel):
whencreated_dt = DateTimeField(null=True)
whenchanged = CharField(max_length=50, null=True)
I load data from a text file to a table using peewee, the column "whenchanged" contains all dates in a format of '%Y-%m-%d %H:%M:%S' as varchar column. Now I want to convert the text field "whenchanged" into a datetime format in "whencreated_dt".
I tried several things... I ended up with this:
# Initialize table to TestTable
to_execute = "table.update({table.%s : datetime.strptime(table.%s, '%%Y-%%m-%%d %%H:%%M:%%S')}).execute()" % ('whencreated_dt', 'whencreated')
which fails with a "TypeError: strptime() argument 1 must be str, not CharField": I'm trying to convert "whencreated" to datetime and then assign it to "whencreated_dt".
I tried a variation... following e.g. works without a hitch:
# Initialize table to TestTable
to_execute = "table.update({table.%s : datetime.now()}).execute()" % (self.name)
exec(to_execute)
But this is of course just the current datetime, and not another field.
Anyone knows a solution to this?
Edit... I did find a workaround eventually... but I'm still looking for a better solution... The workaround:
all_objects = table.select()
for o in all_objects:
datetime_str = getattr( o, 'whencreated' )
setattr(o, 'whencreated_dt', datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S'))
o.save()
Loop over all rows in the table, get the "whencreated". Convert "whencreated" to a datetime, put it in "whencreated_dt", and save each row.
Regards,
Sven
Your example:
to_execute = "table.update({table.%s : datetime.strptime(table.%s, '%%Y-%%m-%%d %%H:%%M:%%S')}).execute()" % ('whencreated_dt', 'whencreated')
Will not work. Why? Because datetime.strptime is a Python function and operates in Python. An UPDATE query works in database-land. How the hell is the database going to magically pass row values into "datetime.strptime"? How would the db even know how to call such a function?
Instead you need to use a SQL function -- a function that is executed by the database. For example, Postgres:
TestTable.update(whencreated_dt=whenchanged.cast('timestamp')).execute()
This is the equivalent SQL:
UPDATE test_table SET whencreated_dt = CAST(whenchanged AS timestamp);
That should populate the column for you using the correct data type. For other databases, consult their manuals. Note that SQLite does not have a dedicated date/time data type, and the datetime functionality uses strings in the Y-m-d H:M:S format.
I have the following custom type on Postgres:
CREATE TYPE my_custom_type AS (
field_a VARCHAR,
field_b NUMERIC(10,3)
);
and the following table:
CREATE TABLE my_table
(
COL1 VARCHAR(120) NOT NULL,
CUSTOM_COLUMN my_custom_type,
CUSTOM_COLUMN_ARRAY my_custom_type[]
);
Everything works fine when I use my custom type with JOOQ:
#Test
public void testWithoutArray(){
MyTableRecord record = dsl.newRecord(MyTable.MY_TABLE);
record.setCol1("My Col1");
MyCustomType customType = new MyCustomType();
customType.setFieldA("Field A Val");
customType.setFieldB(BigDecimal.ONE);
record.setCustomColumn(customType);
record.store();
}
However, when I try to set some value in the field mapped to a custom type array, I have the following error:
#Test
public void testWithArray(){
MyTableRecord record = dsl.newRecord(MyTable.MY_TABLE);
record.setCol1("My Col1");
MyCustomTypeRecord customType = new MyCustomTypeRecord();
customType.setFieldA("Field A Val 1");
customType.setFieldB(BigDecimal.ONE);
MyCustomTypeRecord customType2 = new MyCustomTypeRecord();
customType2.setFieldA("Field A Val 2");
customType2.setFieldB(BigDecimal.TEN);
record.setCustomColumnArray(new MyCustomTypeRecord[]{customType, customType2});
record.store();
}
org.jooq.exception.DataAccessException: SQL [insert into "my_table" ("col1", "custom_column_array") values (?, ?::my_custom_type[]) returning "my_table"."col1"]; ERROR: malformed record literal: "my_custom_type"(Field A Val 1, 1)"
Detail: Missing left parenthesis.
at org.jooq.impl.Utils.translate(Utils.java:1553)
at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:571)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:347)
at org.jooq.impl.TableRecordImpl.storeInsert0(TableRecordImpl.java:176)
at org.jooq.impl.TableRecordImpl$1.operate(TableRecordImpl.java:142)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:123)
at org.jooq.impl.TableRecordImpl.storeInsert(TableRecordImpl.java:137)
at org.jooq.impl.UpdatableRecordImpl.store0(UpdatableRecordImpl.java:185)
at org.jooq.impl.UpdatableRecordImpl.access$000(UpdatableRecordImpl.java:85)
at org.jooq.impl.UpdatableRecordImpl$1.operate(UpdatableRecordImpl.java:135)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:123)
at org.jooq.impl.UpdatableRecordImpl.store(UpdatableRecordImpl.java:130)
at org.jooq.impl.UpdatableRecordImpl.store(UpdatableRecordImpl.java:123)
The query generated by JOOQ debugg is the following:
DEBUG [main] org.jooq.tools.LoggerListener#debug:255 - Executing query : insert into "my_table" ("col1", "custom_column_array") values (?, ?::my_custom_type[]) returning "my_table"."col1"
DEBUG [main] org.jooq.tools.LoggerListener#debug:255 - -> with bind values : insert into "my_table" ("col1", "custom_column_array") values ('My Col1', array[[UDT], [UDT]]) returning "my_table"."col1"
Am I missing some configuration or is it a bug?
Cheers
As stated in the relevant issue (https://github.com/jOOQ/jOOQ/issues/4162), this is a missing piece of support for this kind of PostgreSQL functionality. The answer given in the issue so far is:
Unfortunately, this is an area where we have to work around a couple of limitations of the PostgreSQL JDBC driver, which doesn't implement SQLData and other API (see also pgjdbc/pgjdbc#63).
Currently, jOOQ binds arrays and UDTs as strings. It seems that this particular combination is not yet supported. You will probably be able to work around this limitation by implementing your own custom data type Binding:
http://www.jooq.org/doc/latest/manual/code-generation/custom-data-type-bindings/
The question is as for delphi coders as for c++ builder coders, cuz I'm using the same components.
I'm trying to fill labels on the form by the data from database. I do a SELECT query via TADOQuery. But when I try to get a result, I always get an error like "ADOQuery1: Field 'count' not found".
'id' passed to the function is an autoincrement field value, which is EXACTLY exists in database (it was got via DBLookupComboBox). Also, executing the query manually to show result in DBGrid is successfull.
Querying without parameters and writing 'id' value to query string fails too.
What's the problem? Here's the code.
void TSellForm::LoadData(int id) {
TADOQuery* q = DataModule1->ADOQuery1;
q->Active = false;
try
{
q->SQL->Text = "select * from drugs where(id=:id)";
q->Parameters->ParamByName("id")->Value = IntToStr(id);
q->ExecSQL();
this->LabelAvail->Caption = q->FieldByName("count")->Value;
}
catch (Exception* e) {
MessageBox(NULL, PChar(WideString(e->Message)),
L"Exception", MB_OK|MB_ICONWARNING);
}
q->SQL->Clear();
}
ExecSQL is only used for SQL statements that don't return a recordset, and to determine the results you use RowsAffected.
For SELECT statements (which return a recordset), you use Open or set Active to true.
Also, count is a reserved word in most SQL dialects (as in SELECT Count(*) FROM ..., so if you have a column with that name you're going to need to escape it, typically by using either [] or double-quotes around it or by aliasing it in the SELECT itself.
ADOQuery1->Close();
ADOQuery1->SQL->Text= "SELECT * FROM reportTble WHERE (firstName =:firstName) " ;
ADOQuery1->Parameters->ParamByName("firstName")->Value = textBox->Text ;
ADOQuery1->Open();
This is how you can use ADOQuery
I am trying to select only certain columns from a table using EF 6.1. However, it won't let me pull back just the columns I want. I have to pull back every column from the table which has 14,000 rows so the query takes ~30 seconds. The column that kills the query is a NVARCHAR in the table. But with EF it's all or nothing. I am using IEnumerable also. Perhaps I should be using IQueryable?>
Using this query I get an anonymous type error:
Using db As Ctx = New Ctx
Dim postcount = db.be_Posts.Count
posts = db.be_Posts.Select(Function(S) New With {S.DateCreated, S.Description, S.PostRowID, S.Title}).OrderByDescending(Function(x) x.DateCreated)
Return posts.ToList
End Using
Error:
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery`1[VB$AnonymousType_0`4[System.DateTime,System.String,System.Int32,System.String]]' to type 'System.Collections.Generic.IEnumerable`1
This works but is getting all the records and the columns I don't need:
Using db As Ctx = New Ctx
Dim postcount = db.be_Posts.Count
posts = db.be_Posts.OrderByDescending(Function(x) x.DateCreated).ToList
Return posts
End Using
What I would do is:
Create PostSummaryDto class:
public class PostSummaryDto
{
public DateTime DateCreated { get; set; }
...rest of fields...
}
Use PostSummaryDto class in query:
New PostSummaryDto { DateCreated = S.DateCreated, ...}
Define return type of function as IEnumerable<PostSummaryDto>.
I am not a fan of Visual Basic, so I am not sure if returning anonymous types is allowed, but I believe it is good custom to define return types clearly.