SQL: Change the datetime to the exact string returned - postgresql

See below for what is returned in my automated test for this query:
Select visit_date
from patient_visits
where patient_id = '50'
AND site_id = '216'
ORDER by patient_id
DESC LIMIT 1
08:52:48.406 DEBUG Executing : Select visit_date from patient_visits
where patient_id = '50' AND site_id = '216' ORDER by patient_id DESC
LIMIT 1 08:52:48.416 TRACE Return: [(datetime.date(2017, 2, 17),)]
When i run this in workbench i get
2017-02-17
How can i make the query return this instead of the datetime.date bit above. Some formatting needed?

What you got from the database is python's datetime.date object - and that happens due to the db connector drivers casting the DB records to the their corresponding python counterparts. Trust me, it's much better this way than plain strings the user would have to parse and cast himself later.
Imaging the result of this query is stored in a variable ${record}, there are a couple of things to get to it, in the form you want.
First, the response is (pretty much always) a list of tuples; as in your case it will always be a single record, go for the 1st list member, and its first tuple member:
${the_date}= Set Variable ${record[0][0]}
Now {the_date} is the datetime.date object; there are at least two ways to get its string representation.
1) With strftime() (the pythonic way):
${the_date_string}= Evaluate $the_date.strftime('%Y-%m-%d') datetime
here's a link for the strftime's directives
2) Using the fact it's a regular object, access its attributes and construct the result as you'd like:
${the_date_string}= Set Variable ${the_date.year}-${the_date.month}-${the_date.day}
Note that this ^ way, you'd most certainly loose the leading zeros in the month and day.

Related

JPA: Group by and select function in Postgres

I have a simple reporting query group by id and day that looks like the following:
select id,
avg(case when name = 'temp' then value end) as average_temp,
DATE_TRUNC('day', timestamp) as day
from data
group by id, day
order by id;
The query basically needs to show the average daily temperature for each asset.
The user is able to specify a bunch of different aggregation functions beyond just 'average', the above is only a simple example. For example, avg temp, max temp, max speed, etc.
I'm trying to translate that into JPA as follows:
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<AssetMetricDataPoint> root = query.from(Data.class);
List<Selection<?>> selectionList = getSelections(aggregationQuery, criteriaBuilder, root);
Expression<Instant> groupDate = criteriaBuilder.function("date_trunc", Instant.class, criteriaBuilder.literal("day"), root.get("timestamp"));
selectionList.add(groupDate.alias("day"));
query.multiselect(selectionList);
query.where(getWherePredicates(aggregationQuery, criteriaBuilder, root));
query.orderBy(getOrderBy(aggregationQuery, criteriaBuilder, root));
query.groupBy(root.get("id"), groupDate);
return this.setupPagination(entityManager.createQuery(query), aggregationQuery);
I'm using criteriaBuilder.function to group by the date. However, when I execute the query using JPA I get the following exception:
org.postgresql.util.PSQLException: ERROR: column "data0_.timestamp" must appear in the GROUP BY clause or be used in an aggregate function
This appears to occur because the query is parametized and Postgres doesn't realize that the 'day' parameter that appears in both the select and group by clauses are the same.
Is there any way around this. Can I somehow bake in the 'day' value so it's not sent a parameter? Or some other method?
In the end there's a relatively solution to the problem. Rather than grouping by the expression, I thought I'd try to group by the alias instead: criteriaBuilder.literal("day"). This didn't work, however, with Postgres complaining about a non-integer literal.
I then realised I could group by a positional integer instead, which in my case ended up looking like:
query.groupBy(root.get("id"), criteriaBuilder.literal(selectionList.size()));
This works as expected.

PostgreSql Queries treats Int as string datatypes

I store the following rows in my table ('DataScreen') under a JSONB column ('Results')
{"Id":11,"Product":"Google Chrome","Handle":3091,"Description":"Google Chrome"}
{"Id":111,"Product":"Microsoft Sql","Handle":3092,"Description":"Microsoft Sql"}
{"Id":22,"Product":"Microsoft OneNote","Handle":3093,"Description":"Microsoft OneNote"}
{"Id":222,"Product":"Microsoft OneDrive","Handle":3094,"Description":"Microsoft OneDrive"}
Here, In this JSON objects "Id" amd "Handle" are integer properties and other being string properties.
When I query my table like below
Select Results->>'Id' From DataScreen
order by Results->>'Id' ASC
I get the improper results because PostgreSql treats everything as a text column and hence does the ordering according to the text, and not as integer.
Hence it gives the result as
11,111,22,222
instead of
11,22,111,222.
I don't want to use explicit casting to retrieve like below
Select Results->>'Id' From DataScreen order by CAST(Results->>'Id' AS INT) ASC
because I will not be sure of the datatype of the column due to the fact that JSON structure will be dynamic and the keys and values may change next time. and Hence could happen the same with another JSON that has Integer and string keys.
I want something so that Integers in Json structure of JSONB column are treated as integers only and not as texts (string).
How do I write my query so that Id And Handle are retrieved as Integer Values and not as strings , without explicit casting?
I think your assumtions about the id field don't make sense. You said,
(a) Either id contains integers only or
(b) it contains strings and integers.
I'd say,
If (a) then numerical ordering is correct.
If (b) then lexical ordering is correct.
But if (a) for some time and then (b) then the correct order changes, too. And that doesn't make sense. Imagine:
For the current database you expect the order 11,22,111,222. Then you add a row
{"Id":"aa","Product":"Microsoft OneDrive","Handle":3095,"Description":"Microsoft OneDrive"}
and suddenly the correct order of the other rows changes to 11,111,22,222,aa. That sudden change is what bothers me.
So I would either expect a lexical ordering ab intio, or restrict my id field to integers and use explicit casting.
Every other option I can think of is just not practical. You could, for example, create a custom < and > implementation for your id field which results in 11,111,22,222,aa. ("Order all integers by numerical value and all strings by lexical order and put all integers before the strings").
But that is a lot of work (it involves a custom data type, a custom cast function and a custom operator function) and yields some counterintuitive results, e.g. 11,111,22,222,0a,1a,2a,aa (note the position of 0a and so on. They come after 222).
Hope, that helps ;)
If Id always integer you can cast it in select part and just use ORDER BY 1:
select (Results->>'Id')::int From DataScreen order by 1 ASC

PostgreSQL query has = operators inside SELECT column references, what does this syntax mean?

SQLALchemy generated the following query for me:
SELECT count(client.id = user_accounting_journal_entry.client_id) AS count_1
FROM client, user_accounting_journal_entry
WHERE user_accounting_journal_entry.kind = 'debit'
GROUP BY client.name = user_accounting_journal_entry.client_id
Note the part inside select: count(client.id = user_accounting_journal_entry.client_id).
Having mostly used MySQL, I am not familiar with this syntax, and have a hard time finding documentation.
You should be familiar with the syntax from MySQL, at least in this form:
select sum(client.id = user_accounting_journal_entry.client_id)
This would count the number of matches.
The count() version counts the number of times that the expression is not NULL. Or equivalent, it counts the number of times that both values are not NULL . . . something that seems really strange. More commonly in Postgres, I would expect:
select sum((client.id = user_accounting_journal_entry.client_id)::int)
This converts the boolean to an integer and hence counts the number of matches.
The query itself is awful:
It doesn't use proper join syntax
The join conditions between the tables don't look correct (a name to an id)
It is grouping by a boolean condition
In addition, it doesn't look like it is doing something that is really useful.
count(client.id = user_accounting_journal_entry.client_id) count number of times expression is not null.

Are there different kinds of NULLs?

This came from
<cfquery name="data">
SELECT
NULL AS plainNull,
CAST(NULL AS datetime) AS Due_Date
</cfquery>
Are these two different kinds of nulls? What is the difference
Note: adapted from
How can I call a function in another CFC file from within a query of a function in one cfc file?
Yes, there is a difference - but not the way you may be thinking.
It is not that NULL has a data type per se, one will be associated with it (implicitly or explicitly) as part of evaluating the SQL. With the SELECT statement above, the data type applies to the column metadata. Without an explicit CAST, the data type defaults to INT.
Take this query:
<cfquery name="qTest" datasource="#variables.sqlServerDSN#">
SELECT NULL AS NonSpecifiedNull
, CAST(NULL AS datetime) AS DateTimeNull
, CAST(NULL AS varchar(25)) AS VarcharNull
</cfquery>
<cfdump var="#getMetaData(qTest)#" label="Query MetaData">
If you examine the query metadata, the query contains columns with three distinct data types: integer, datetime and varchar:
From a ColdFusion standpoint, the data type impacts how the column values may be interpreted in various functions, comparisons, QoQ's and even when modifying the query object itself. For example, update each of the three columns with a date time object:
<cfset dateTimeNow = now()>
<cfset qTest.NonSpecifiedNull[1] = dateTimeNow>
<cfset qTest.DateTimeNull[1] = dateTimeNow>
<cfset qTest.VarcharNull[1] = dateTimeNow>
<cfdump var="#qTest#" label="Query Values">
The results will be very different. The value inserted into "DateTimeNull" remains a date object. However, the same value will be converted into a string when inserted into "VarcharNull" and an integer when inserted into "NonSpecifiedNull". The latter two are almost certainly not how you would expect, or want, date objects to be handled. That is why it is better to include the appropriate CAST in the original SQL query.
Granted ColdFusion is very forgiving, and relatively typeless. So storing date objects as a string or integer may not always cause hard errors. However, it does force implicit conversion, which is at best unnecessary, at worst prone to error and/or likely to produce the wrong result. For example, if you compared original date to the "NonSpecifiedNull" column, you would probably expect them to be equal, but .. they are not, due to the data type:
dateCompare(dateTimeNow, qTest.NonSpecifiedNull[1]) eq 0 ? "EQUAL" : "DIFFERENT"
So again, best to CAST to the correct type up front to ensure the expected results.

How to avoid T-SQL function being called more times when needing combined results?

I have two T-SQL scalar functions that both perform calculations over large sums of data (taking 'a lot' of time) and return a value, e.g. CalculateAllIncomes(EmployeeID) and CalculateAllExpenditures(EmployeeID).
I run a select statement that calls these and returns results for each Employee. I also need the balance of each employee calculated as AllIncomes-AllExpenditures.
I have a function GetBalance(EmployeeID) that calls the two above mentioned functions and returns the result {CalculateAllIncomes(EmployeeID) - CalculateAllExpenditures(EmployeeID)}. But if I do:
Select CalculateAllIncomes(EmployeeID), CalculateAllExpenditures(EmployeeID), GetBalance(EmployeeID) .... the functions CalcualteAllIncomes() and CalculateAllExpenditures get called twice (once explicitly and once inside the GetBalance funcion) and so the resulting query takes twice as long as it should.
I'd like to find some better solution. I tried:
select alculateAllIncomes(EmployeeID), AS Incomes, CalculateAllExpenditures
(EmployeeID) AS Expenditures, (Incomes - Expenditures) AS Balance....
but it throws errors:
Invalid column name Incomes and
Invalid column name Expenditures.
I'm sure there has to be a simple solution, but I cannot figure it out. For some reason it seems that I am not able to use column Aliases in the SELECT clause. Is it so? And if so, what could be the workaround in this case?
Thanks for any suggestions.
Forget function calls: you can probably do it everything in one normal query.
Function calls misused (trying for OO encapsulation) force you into this situation. In addition, if you have GetBalance(EmployeeID) per row in the Employee table then you are CURSORing over the table. And you've now compounded this by multiple calls too.
What you need is something like this:
;WITH cSUMs AS
(
SELECT
SUM(CASE WHEN type = 'Incomes' THEN SomeValue ELSE 0 END) AS Income),
SUM(CASE WHEN type = 'Expenditures' THEN SomeValue ELSE 0 END) AS Expenditure)
FROM
MyTable
WHERE
EmployeeID = #empID --optional for all employees
GROUP BY
EmployeeID
)
SELECT
Income, Expenditure, Income - Expenditure
FROM
cSUMs
I once got a query down from a weekend to under a second by eliminating this kind of OO thinking from a bog standard set based aggregate query.