GetColumName(i) returns "" when using UNION (Jaybird) - java

I'm a little bit confused with my new SQL query using a union.
I request my firebird db in a java app using jaybird 2.2.8. Usually I parse my ResultSet using the MetaData
ResultSetMetaData metaData = resultSet.getMetaData();
and looping through the columns
for (int i = 1; i <= metaData.getColumnCount(); i++) {
columnName = metaData.getColumnName(i);
int columnType = metaData.getColumnType(i);
switch (columnType) { ...
This was working quite well until I started using a Union in my SQL query.
Now the method
metaData.getColumnName(i)
returns an empty string instead of the column name - the column type is valid.
When I use a SQL query without Union everything works as expected and when I test my query in IBExpert all columns have a a valid name.
Any idea whats wrong? Does anybody has a workaround?
Btw. the ResultSet looks quite well in the eclipse debugger

Instead of the getColumnName you need to use getColumnLabel. The column name only has a value if the value is guaranteed to come from a single column in a single table, which is not the case in a UNION. In the case of a UNION, Firebird handles the name of columns (or the alias if specified) in the first select as aliases (labels) only; it doesn't preserve the column name as a column name that can be returned by getColumnName.
If you really need to use getColumnName (eg for compatibility reasons), then you can use the Jaybird connection property columnLabelForName=true, but this is discouraged because it is not compliant with JDBC. In almost all cases you should use getColumnLabel instead.
The distinction between column name and column label is subtle, and wasn't really clear in older JDBC specification. Basically column name is the name of the column in the original table, while the column label is the 'name' (or more correctly: label) of the column in the result set. The label is either the alias specified with AS, or - if no alias is specified - the original column name as returned by getColumnName.
See also:
Changes and fixes in Jaybird 2.2.1
Compatibility with com.sun.rowset.*
Disclosure: I am one of the Jaybird developers

Related

How to get Column Comments in JDBC

I want to fetch Column comments using JDBC Metadata , But everytime it returns null , I tested with Oracle and SqlServer both cases it returning Null.
DatabaseMetaData dmt = con.getMetaData();
colRs = dmt.getColumns(null, "dbo", 'Student', null);
while (colRs.next()) {
System.out.println(colRs.getString("REMARKS");
}
While i am getting all other data like column name , length etc absolutely ok ...
For Oracle you need to provide a connection property remarksReporting and set that to true or call the method setRemarksReporting() to enable that.
OracleConnection oraCon = (OracleConnection)con;
oraCon.setRemarksReporting(true);
After that, getColumns() will return the column (or table) comments in the REMARKS column of the ResultSet.
See Oracle's JDBC Reference for more details
For SQL Server this is not possible at all.
Neither the Microsoft nor the jTDS driver expose table or column comments. Probably because there is no SQL support for that in SQL Server. The usual approach of using "extended properties" and the property name MS_DESCRIPTION is not reliable. Mainly because there is no requirement to us MS_DESCRIPTION as the property name. Not even sp_help returns those remarks. And at least the jTDS driver simply calls sp_help go the the table columns. I don't know what the Microsoft driver does.
The only option you have there, is to use fn_listextendedproperty() to retrieve the comments:
e.g.:
SELECT objname, cast(value as varchar(8000)) as value
FROM fn_listextendedproperty ('MS_DESCRIPTION','schema', 'dbo', 'table', 'Student', 'column', null)
You need to replace MS_DESCRIPTION with whatever property name you use to store your comments.

Query that returns the column type integer for a given table

JDBC supports the following:
ResultSet columns = connection.getMetaData().getColumns(null, schema, tableName, null);
This way I can retrieve information for all columns in the given table including the DATA_TYPE information. That information is of type int in java.
I used the following query to retrieve column name and data type but this returns the string representation of data type and I need int :
select column_name, data_type from all_tab_cols where table_name = 'tablename' and owner = 'owner';
How does the line of java code translate to a query in Oracle DB ?
How can I retrieve the int representation of a column ?
I would use a join but I don't know where to get the information from. Where does Oracle store these informations ?
For running SQL against a DB without using any ORM tools like hibernate, you can take a look at PreparedStatement class.
If you want to look at the "metadata" about the columns your PreparedStatement returned, you can take a look at the getMetaData() method, documentation can be found here and then ask the ResultSetMetaData about the column type: getColumnType(<0 based index of the column you want to find the type>) documentation can be found here
Regarding your question about how Java translates to Query in Oracle, it is too broad to be described in an answer. I would recommend looking for a tutorial on JDBC or the official tutorial.

How to make the resultset returned from oracle keeps its column aliases characters case

I am trying to querying some sql statment againest oracle database.
I am using Java ResultSetMetaData to get column alias names (through: rsmd.getColumnLable() )
The query looks like:
select part_id partId, part_num partNumber from tbl;
But the result set meta-data returns for me the aliases as partid and partnumber respectively ...
But I need to get the aliases in the same characters case the user choose, so I need to get it as partId and partNumber respectively.
How to accomplish that?
Thanks.
Column names and aliases are case insensitive by default, if you want to preserve case in an oracle statement you could quote the names like this:
select part_id "partId", part_num "partNumber" from tbl;
In my tests the column names where returned in uppercase when not using the quotes, so the behaviour might also depend on the version of the jdbc driver.

How to invertly select columns in sql?

If I have a SQL table with columns:
NR_A, NR_B, NR_C, NR_D, R_A, R_B, R_C
and on runtime, I add columns following the column's sequence such that the next column above would be R_D followed by R_E.
My problem is I need to reset the values of columns that starts with R_ (labeled that way to indicate that it is resettable) back to 0 each time I re-run my script . NR_ columns btw are fixed, so it is simpler to just say something like:
UPDATE table set col = 0 where column name starts with 'NR_'
I know that is not a valid SQL but I think its the best way to state my problem.
Any thoughts?
EDIT: btw, I use postgres (if that would help) and java.
SQL doesn't support dynamically named columns or tables--your options are:
statically define column references
use dynamic SQL to generate & execute the query/queries
Java PreparedStatements do not insulate you from this--they have the same issue, just in Java.
Are you sure you have to add columns during normal operations? Dynamic datamodels are most of the time a realy bad idea. You will see locking and performance problems.
If you need a dynamic datamodel, take a look at key-value storage. PostgreSQL also has the extension hstore, check the contrib.
If you don't have many columns and you don't expect the schema to change, just list them explicitly.
UPDATE table SET NR_A=0;
UPDATE table SET NR_B=0;
UPDATE table SET NR_C=0;
UPDATE table SET NR_D=0;
Otherwise, a simple php script could dynamically build and execute your query:
<?php
$db = pg_connect("host=localhost port=5432 user=postgres password=mypass dbname=mydb");
if(!$db) die("Failed to connect");
$reset_cols = ["A","B","C","D"];
foreach ($col in $reset_cols) {
$sql = "UPDATE my_table SET NR_" . $col . "=0";
pg_query($db,$sql);
}
?>
You could also lookup table's columns in Postgresql by querying the information schema columns tables, but you'll likely need to write a plpgsql function to loop over the query results (one row per table column starting with "NR_").
if you rather using sql query script, you should try to get the all column based on given tablename.
maybe you could try this query to get all column based on given tablename to use in your query.
SELECT attname FROM
pg_attribute, pg_type
WHERE typname = 'tablename' --your table name
AND attrelid = typrelid
AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', 'tableoid', 'xmin', 'xmax')
--note that this attname is sys column
the query would return all column with given tablename except system column

JDBC DatabaseMetaData.getColumns() returns duplicate columns

I'm busy on a piece of code to get alle the column names of a table from an Oracle database. The code I came up with looks like this:
DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:#<server>:1521:<sid>", <username>, <password>);
DatabaseMetaData meta = conn.getMetaData();
ResultSet columns = meta.getColumns(null, null, "EMPLOYEES", null);
int i = 1;
while (columns.next())
{
System.out.printf("%d: %s (%d)\n", i++, columns.getString("COLUMN_NAME"),
columns.getInt("ORDINAL_POSITION"));
}
When I ran this code to my surprise too many columns were returned. A closer look revealed that the ResultSet contained a duplicate set of all the columns, i.e. every column was returned twice. Here's the output I got:
1: ID (1)
2: NAME (2)
3: CITY (3)
4: ID (1)
5: NAME (2)
6: CITY (3)
When I look at the table using Oracle SQL Developer it shows that the table only has three columns (ID, NAME, CITY). I've tried this code against several different tables in my database and some work just fine, while others exhibit this weird behaviour.
Could there be a bug in the Oracle JDBC driver? Or am I doing something wrong here?
Update: Thanks to Kenster I now have an alternative way to retrieve the column names. You can get them from a ResultSet, like this:
DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:#<server>:1521:<sid>", <username>, <password>);
Statement st = conn.createStatement();
ResultSet rset = st.executeQuery("SELECT * FROM \"EMPLOYEES\"");
ResultSetMetaData md = rset.getMetaData();
for (int i=1; i<=md.getColumnCount(); i++)
{
System.out.println(md.getColumnLabel(i));
}
This seems to work just fine and no duplicates are returned! And for those who wonder: according to this blog you should use getColumnLabel() instead of getColumnName().
In oracle, Connection.getMetaData() returns meta-data for the entire database, not just the schema you happen to be connected to. So when you supply null as the first two arguments to meta.getColumns(), you're not filtering the results for just your schema.
You need to supply the name of the Oracle schema to one of the first two parameters of meta.getColumns(), probably the second one, e.g.
meta.getColumns(null, "myuser", "EMPLOYEES", null);
It's a bit irritating having to do this, but that's the way the Oracle folks chose to implement their JDBC driver.
This doesn't directly answer your question, but another approach is to execute the query:
select * from tablename where 1 = 0
This will return a ResultSet, even though it doesn't select any rows. The result set metadata will match the table that you selected from. Depending on what you're doing, this can be more convenient. tablename can be anything that you can select on--you don't have to get the case correct or worry about what schema it's in.
In the update to your question I noticed that you missed one key part of Kenster's answer. He specified a 'where' clause of 'where 1 = 0', which you don't have. This is important because if you leave it off, then oracle will try and return the ENTIRE table. And if you don't pull all of the records over, oracle will hold unto them, waiting for you to page through them. Adding that where clause still gives you the metadata, but without any of the overhead.
Also, I personally use 'where rownum < 1', since oracle knows immediately that all rownums are past that, and I'm not sure if it's smart enough to not try and test each record for '1 = 0'.
In addition to skaffman's answer -
use the following query in Oracle:
select sys_context( 'userenv', 'current_schema' ) from dual;
to access your current schema name if you are restricted to do so in Java.
This is the behavior mandated by the JDBC API - passing nulls as first and second parameter to getColumns means that neither catalog name nor schema name are used to narrow the search.
Link to the documentation . It is true that some other JDBC drivers have different behavior by default (e.g MySQL's ConnectorJ by default restricts to the current catalog), but this is not standard, and documented as such

Categories