MySQL enum datatype access in clojure - java

I am trying to write a simple application which reads the a database and produces a set of functions with which to access it; so far so good. Now, what I have come across is that some of the columns in my database are defined as MySQL enum types (e.g. ENUM('red','green','violet')) and I would like to validate the stuff I send to the database rather than receive an error from the driver when an unacceptable value is given, so I was wondering if there is a way to retrieve the possible values for the enum from within clojure.
I am using [clojure.java.jdbc "0.3.0-alpha5"] and [mysql/mysql-connector-java "5.1.25"]. In order to get the metadata for the table I am currently using java.sql.DatabaseMetaData, but trying .getPseudoColumns just gives me nil every time.

Turns out there is no straight forward way to do this using libraries. My own solution is:
(defn- parse-enum
"Parses an enum string and returns it's components"
[enum-str]
; "enum('temp','active','canceled','deleted')"
(map (comp keyword #(.replace % "'" ""))
(-> enum-str
(.replaceFirst "^[^\\(]+\\(([^\\)]+)\\)$" "$1")
(.split "'?,'?"))))
(defn get-enum-value
"Returns the values for an enum in a table.column"
[table column]
(jdbc/with-connection db
(jdbc/with-query-results rs
[(str "show columns from " table " where field = ?") column]
((comp set parse-enum :type first) rs))))

Related

jOOQ Postgres PERCENTILE_CONT & MEDIAN Issue with Type Casting

Coercion of data types does not seem to work within median() or percentileCont(). Data type coercion works just fine with other aggregate functions like max() and min(). The Postgres queries that are produced as a result show that type casting is not applied in the final result. Below are the snippets from jOOQ and Postgres for reference. As of now, I have no work-around or knowledge of an open ticket for this issue.
Any direction would be much appreciated!
MEDIAN
jOOQ Snippet
selectFields.add(
median(
field(String.format("%s.%s", a.getDataSourceName(), a.getField()))
.coerce(Double.class)) // Seems to not successfully coerce data types
.as(
String.format(
"%s.%s.%s", a.getDataSourceName(), a.getField(), "median")));
SQL Output
select
tableA.columnA,
percentile_cont(0.5) within group (order by tableA.columnA) as "tableA.columnA.median"
from tableA
group by tableA.columnA
limit 100;
ERROR: function percentile_cont(numeric, text) does not exist
PERCENTILE_CONT
jOOQ Snippet
selectFields.add(
percentileCont(a.getPercentileValue())
.withinGroupOrderBy(
field(String.format("%s.%s", a.getDataSourceName(), a.getField()))
.coerce(Double.class)) // Seems to not successfully coerce data types
.as(
String.format(
"%s.%s.%s", a.getDataSourceName(), a.getField(), "percentile_" + Math.round(a.getPercentileValue() * 100))));
SQL Output
select
tableA.columnA,
percentile_cont(0.0) within group (order by tableA.columnA) as "tableA.columnA.percentile_0"
from tableA.columnA
group by tableA.columnA
limit 100;
ERROR: function percentile_cont(numeric, text) does not exist
POSTGRES -- This works due to type casting
select
percentile_cont(0.5)
within group (
order by tableA.columnA::INTEGER
)
as "tableA.columnA.median"
from tableA.columnA
group by (select 1)
https://www.jooq.org/javadoc/latest/org.jooq/module-summary.html
You're not looking for coercion, which in jOOQ-speak means changing a data type only in the client without letting the server know. This is mostly useful when fetching data of some type (e.g. Integer) despite jOOQ producing some other data type (e.g. BigInteger), otherwise. See the Javadoc on Field.coerce()
Unlike with casting, coercing doesn't affect the way the database sees a Field's type.
// This binds an int value to a JDBC PreparedStatement
DSL.val(1).coerce(String.class);
// This binds an int value to a JDBC PreparedStatement
// and casts it to VARCHAR in SQL
DSL.val(1).cast(String.class);
Cleary, you want to Field.cast(), instead, just like in your example where you actually used a cast tableA.columnA::INTEGER.

Can we change content of powerbi report using powerbi rest

Can we change content of Power BI report using Power BI REST API, say in
some report 'Col A' has been used and I want to change it to 'Col
B'. 'Col A' and 'Col B' are in same dataset and same table.
In updatereportcontent API, what can be the possible value of
sourcetypeenum apart from 'ExistingReport'?
https://learn.microsoft.com/en-us/rest/api/power-bi/reports/updatereportcontent#sourcetypeenum
I am using Power BI native application to do this task.
First about question #2 - as you can see in the link you gave, ExistingReport is the only possible value.
About your first question - you can't do this directly using the API. However, you can use a text value parameter (let's name it ColName) to hold the name of the column you want to select. Modify the M query for fetching the data from the database by changing it from:
let
Source = Sql.Database(ServerName, DatabaseName, [Query="select Col1, Col2, ColA from Sales.Orders"])
in
Source
to:
let
Source = Sql.Database(ServerName, DatabaseName, [Query="select Col1, Col2, " & ColName & " as ColA from Sales.Orders"])
in
Source
Then use Update Parameters or Update Parameters In Group API to change the value of ColName parameter (to let's say ColB). If this is imported dataset, you must refresh it using Refresh Dataset or Refresh Dataset In Group after that.

Alias with two Fields using JOOQ

i have a nasty SQl that i want transform in JOOQ
Here are the Query:
SELECT
SUM(dpr.dpr_bruttopraemie_prt + dpr.dpr_sofortrabatt_prt)
, MAX(TO_NUMBER(DP1.dp_wert))
FROM deckungen deck, deckungspraemien dpr,
(SELECT dp.dp_id, dp.dp_wert
FROM textbausteine txb, druckparameter dp
WHERE dp.dp_txb_id = txb.txb_id
) DP1
WHERE DP1.dp_id = :druckparameter_id;
As you can see, i need to make alias from a select with two Fields.
dp.dp_id, dp.dp_wert
that im going to used it on some other parts.
How i can i get it done?
i've seen
.asField()
Funktion but it only make alias for one column.
PS: The actual Query are a lot more complicated. So i wrote a simpler one.
With hoping that it's satisfied the SQL ORACLE Dialect.
I'm assuming that you're using the code generator, so you have generated objects available for your tables like DECKUNGEN. I'm also assuming you're using these static imports:
import static org.jooq.impl.DSL.*; // The jOOQ API
import static com.example.myapp.generated.Tables.*; // Your generated tables
You can then write:
Deckungen deck = DECKUNGEN.as("deck");
Deckungspraemien dpr = DECKUNGSPRAEMIEN.as("dpr");
Textbausteine txb = TEXTBAUSTEINE.as("txb");
Druckparameter dp = DRUCKPARAMETER.as("dp");
Table<?> dp1 = table(
select(dp.DP_ID, dp.DP_WERT)
.from(txb, dp)
.where(dp.DP_TXB_ID.eq(txb.TXB_ID))
).as("dp1");
Record2<BigDecimal, BigDecimal> result =
using(configuration)
.select(
sum(dpr.DPR_BRUTTOPRAEMIE_PRT.plus(dpr.DPR_SOFORTRABATT_PRT)),
max(field("to_number({0})", BigDecimal.class, dp1.field(dp.DP_WERT))))
.from(deck, dpr, dp1)
.where(dp1.field(dp.DP_ID).eq(druckparameterId))
.fetchOne();
Some explanations
jOOQ currently doesn't have a built-in TO_NUMBER() function, but you can easily roll your own using DSL.field(String) and similar overloads. For more detail, refer to the manual's section about plain SQL
A derived table can be created most easily by using the DSL.table(Select) operator.
Columns from a derived table can be dereferenced using Table.field() methods, in particular the Table.field(Field), which tries to find a field by the same name as the argument field, retaining the argument field's type information.
Side-note
I don't think your query is correct as you're simply creating cartesian products between your tables deck, dpr, and dp1. Specifically, the SUM() is quite likely to be wrong, whereas MAX() is simply calculated inefficiently.

Workaround in UCanAccess for multi-value fields: "incompatible data type in conversion: from SQL type OTHER"?

I am trying to use UCanAccess to query a MS Access .accdb file. Everything works great, except when I query multi-value fields. For example those that have entries in the Row Source of a table field's Lookup tab in design view in MS Access. My code crashes when I try to output the result:
ResultSet rslt = stmt.executeQuery("SELECT [singleValue], [multiValue] FROM [TableName];");
int count = 0;
while (rslt.next())
System.out.println(count++ + "\t" + rslt.getString(1) + "\t" + rslt.getString(2));
The ResultSet is returned fine, and the singleValue prints fine, but the following error is thrown if I try to print the multiValue from the ResultSet:
Exception in thread "main" net.ucanaccess.jdbc.UcanaccessSQLException: incompatible data type in conversion: from SQL type OTHER to java.lang.String, value: instance of org.hsqldb.types.JavaObjectData
I have tried querying a query that is stored in the .accdb, but that does not work, because it just triggers the original query, and returns the same ResultSet.
Am I missing something simple or is this something UCanAccess can not handle?
This is the first question about it I have ever seen.
You can see an example of the complex types usage with UCanAccess in the ucanaccess web site, tab "Getting Started" (at the end of the page).
Here's a junit test case:
https://sourceforge.net/p/ucanaccess/code/HEAD/tree/ucanaccess/trunk/src/test/java/net/ucanaccess/test/ComplexTest.java
(see the testComplex method).
In particular you can't call rslt.getString(2) but have to use rslt.getObject(2) .
You'll get a ucanaccess wrapper of your data.
If you wanted to get string that described the data content you can use
rslt.getObject(2).toString().
The wrapping classes are:
net.ucanaccess.complex.Attachment,
net.ucanaccess.complex.SingleValue,
net.ucanaccess.complex.Version.
In your example, rslt.getObject(2) should return an array of net.ucanaccess.complex.SingleValue.
Then you can call the method singleValue.getValue() on each array element to get the wrapped value.

Automatically generated database requests

How do you implement automatically generated database (let it be SQL) requests?
Let us have offline shop with filters:
The database is standalone offline.
SO if I want to filter items by Price the request would be something like:
select Snowboard.Name
from Snowboard
where Snowboard.Price between 400 and 600;
And if I filter by two characteristics e.g. Price from and Camber. There would be:
select s.Name, s.Camber
from Snowboard s
where s.Price between 400 and 600
and s.Camber in ('Rocker', 'Hybrid');
The question is how could it be implemented in Java so that these requests are generated automatically from any combination of filters selected?
Quick and dirty solution #1
Generate a query at run time & make clever use of WHERE 1=1 condition as the number of where clause are unknown. (This sample is in C# but works more or less the same with JAVA as well)
string sql= #"select Snowboard.Name
from Snowboard
where 1=1";
Now you can build your query based on the UI element selections like
string whereClause="";
if(yourCheckBoxPrice.Checked)
{
whereClause+= " AND Price BETWEEN "+ txtPriceFrom.Text + " AND "+ txtPriceTo.Text;
}
if(yourCheckBoxCamber.Checked)
{
whereClause+= " AND Camber IN ("+ /* your list of values go here */ +")";
}
sql += whereClause;
2nd Solution (Use SQL CASE)
You can use SQL CASE inside your query for each where clause to check for nulls or specific values. But beware, dynamic SQL will make your code pretty messy & hard to read (Can be done via a stored procedure as well)
SQL- CASE Statement
I advise you to use a stored procedure with a mix of both options 1 and 2. Implementing Dynamic SQL Where Clause. Keep it simple and you are good to go.

Categories

Resources