Can I specify the collation when performing a SELECT INTO - tsql

If I'm selecting from one source into another can I specify the collation at the same time.
e.g.
SELECT Column1, Column2
INTO DestinationTable
FROM SourceTable
Where 'DestinationTable' doesn't already exist.
I know I can do something like
SELECT Column1, Column2 COLLATE Latin1_General_CI_AS
INTO DestinationTable
FROM SourceTable
In my real problem the data types of the column aren't known in advance so I can't just add the collation to each column. It's in a corner of a legacy application using large nasty stored procedures that generate SQL and I'm trying to get it working on a new server that has a different collation in tempdb with minimal changes.
I'm looking for something like:
SELECT Column1, Column2
INTO DestinationTable COLLATE Latin1_General_CI_AS
FROM SourceTable
But that doesn't work.

Can you create the table first?
You can define a collation for the relevant columns. On INSERT, they will be coerced.
It sounds like you don't know the structure of the target table though... so then no, you can't without dynamic SQL. Which will make things worse...

You can do this like that if it helps:
SELECT *
INTO DestinationTable
FROM
(
SELECT Column1 COLLATE Latin1_General_CI_AS, Column2 COLLATE Latin1_General_CI_AS
FROM SourceTable
) as t

To correct Kasia's answer:
SELECT *
INTO DestinationTable
FROM
(
SELECT Column1 COLLATE Latin1_General_CI_AS as Column1
,Column2 COLLATE Latin1_General_CI_AS as Column1
FROM SourceTable
) as t
You have to add an alias for each column to get this work.

Related

Oracle vs Postgres order by

I am running below query in Oracle and Postgres, both shows different output with respect to ordering of the values.
with test as (
select 'Summary-Account by User (Using Contact ID)' col1 from dual
union all
select 'Summary-Account by User by Client by Day (Using Contact ID)' col1 from dual
)
select * from test
order by col1 desc;
Below is Oracle one
Postgres
with test as (
select 'Summary-Account by User (Using Contact ID)' col1
union all
select 'Summary-Account by User by Client by Day (Using Contact ID)' col1
)
select * from test
order by col1 desc;
Oracle collation is AL32UTF8
Postgres has LC_CTYPS is en_US.UTF-8
Both of them look same from how database should behave. How to fix this?
I have read few posts on stackoverflow about POSIX and C, after changing the query order by to order by col1 collate "C" desc; The result matches Oracle output.
Is there anyway to apply this permanently?
AL32UTF8 is not a collation, but an encoding (character set).
Oracle uses the “binary collation” by default, which corresponds the the C or POSIX collation in PostgreSQL.
You have several options to get a similar result in PostgreSQL:
create the database with LOCALE "C"
if you are selecting from a table, define the column to use the "C" collation:
ALTER TABLE tab ALTER col1 TYPE text COLLATE "C";
add an explicit COLLATE clause:
ORDER BY col1 COLLATE "C"

PostgreSQL, CREATE TABLE AS with predefined column(s)

For a first time I find very handy way for importing "last year data" to "this year data".
This works well:
DROP TABLE IF EXISTS mytable;
CREATE TABLE mytable AS
SELECT col1, col2, col3, col4
FROM dblink('host=localhost port=xxxx user=xxxx password=xxxx dbname=mylastyeardb',
'SELECT col1, col2, col3, col4
FROM mytable
WHERE TRIM(col1)<>'''' ')
AS x(col1 text, col2 text, col3 text, col4 text);
ALTER TABLE mytable ADD COLUMN cols_id SERIAL PRIMARY KEY;
Since 'cols_id' from old table is not appropriate for a new table maybe some of experienced users know how to setup a table in CREATE TABLE AS that it have 'cols_id' as (serial) primary key nice ordered and as a first column. Maybe such way I can avoid using of second (ALTER) command?
Any other advice for showed situation will be welcome too.
you either create table, defining its structure (with all handy shortcuts and options in one statement), or create table as select, "inheriting" [partially] the structure. Thus if you want primary key, you will need alter tabale any way...
To put id as first column in one statement, you can simply use a dummy value, eg sequential number:
t=# create table s as select row_number() over() as id,chr(n) from generate_series(197,200) n;
SELECT 4
t=# select * from s;
id | chr
----+-----
1 | Å
2 | Æ
3 | Ç
4 | È
(4 rows)
Of course after that you still need to create sequence, assign its value as default to the id column and add primary key on ot. Which makes it even more statements then you have ATM...

Create a new table from Union two tables with union in postgres

I would like to create a new table as the result of the union of two tables without duplicates. I searched in stackoverflow and I found a question with exactly what I want but using mysql Create a new table from merging two tables with union.
Solution in mysql
CREATE TABLE new_table
SELECT * FROM table1
UNION
SELECT * FROM table2;
I tried to do something similar but I got:
SQL error.
I would like to achieve this if is possible with an statement similar to mysql.
I know that if you create a new table first with the fields that I want. I can do a select into this table over the union of this tables. If there aren't other option well I have to do something like this.
But in summary If possible to do something similar to the question with mysql in postgres. I would like to use syntactic sugar to do that
Thanks in advance
Update
In order to clarify I have two table with equal structure
TABLE1(id,field1,field2,field3)
TABLE2(id,field1,field2,field3)
and The table that I want
TABLE3(id,field1,field2,field3)
Notice that I tried
CREATE TABLE new_table as
SELECT * FROM table1
UNION
SELECT * FROM table2;
and it works but didn't put the fields in the correct place for example put field3 of table 1 in field 1 of table_result
You are missing the AS keyword:
CREATE TABLE new_table
AS
SELECT * FROM table1
UNION
SELECT * FROM table2;
If you need the columns in a specific order, then specify them in the select:
CREATE TABLE new_table
AS
SELECT id, column1, column2, column3
FROM table1
UNION
SELECT id, column1, column2, column3
FROM table2;
More details in the manual:
https://www.postgresql.org/docs/current/static/sql-createtableas.html

Firebird sort order with other character set

The sorting in this query does not take in account signs, only letters:
SELECT CAST(Text AS VARCHAR(20) CHARACTER SET ISO8859_1) COLLATE NO_NO Result FROM (
select CAST('_Anon' AS VARCHAR(20)) COLLATE UNICODE_CI_AI as Text from RDB$DATABASE
UNION
SELECT CAST('Abba' AS VARCHAR(20)) COLLATE UNICODE_CI_AI AS Text from RDB$DATABASE
UNION
SELECT CAST('Beatles' AS VARCHAR(20)) COLLATE UNICODE_CI_AI AS Text from RDB$DATABASE)
ORDER BY Result
Expected sort order(non-alpha-numeric before any letter):
_Anon
Abba
Beatles
But I get:
Abba
_Anon
Beatles
The collation does not matter. If you delete "COLLATE NO_NO" it still sorts wrong.
Edit: Found that collation ES_ES sorts this correct, but it fails to sort Norwegian characters.
Is this a bug or am I missing something in this query?
What I'm trying to do is to get correct sort order in Norwegian, and none of the collations in UNICODE_CI_AI gives me the correct order.
Update: Expanded the example with another sub-query so that it clearer shows the point.
Marks hint to look at the collation pointed me in the direction of a solution.
I do consider this a bug, so I was going to file a a bug report to firebirdsql, but found out it's a "Won't fix" and the workaround below is the official fix.
Of all base collations defined ES_* is the only one with the attribute: SPECIALS-FIRST=1 set. In fact it's the only collation with any attribute set.
And that attribute defines that special characters should be sorted before alphanumeric characters.
So the workaround is to create a new collation based on the NO_NO collation:
CREATE COLLATION NO_NO_NOPAD_CI_SF
FOR ISO8859_1
FROM NO_NO
NO PAD
CASE INSENSITIVE
'SPECIALS-FIRST=1';
then using the new collation like this:
SELECT CAST(Text AS VARCHAR(20) CHARACTER SET ISO8859_1) COLLATE NO_NO_NOPAD_CI_NUM_SF Result FROM (
select CAST('_Anon' AS VARCHAR(20)) COLLATE UNICODE_CI_AI as Text from RDB$DATABASE
UNION
SELECT CAST('Abba' AS VARCHAR(20)) COLLATE UNICODE_CI_AI AS Text from RDB$DATABASE
UNION
SELECT CAST('Beatels' AS VARCHAR(20)) COLLATE UNICODE_CI_AI AS Text from RDB$DATABASE)
ORDER BY Result
Yields the expected result:
_Anon
Abba
Beatles

select where not exists excluding identity column

I am inserting only new records that do not exist in a live table from a "dump" table. My issue is there is an identity column that I don't want to insert into the live, I want the live tables identity column to take care of incrementing the value but I am getting an insert error "Insert Error: Column name or number of supplied values does not match table definition." Is there a way around this or is the only fix to remove the identity column all together?
Thanks,
Sam
You need to list of all the needed columns in your query, excluding the identity column.
One more reason why you should never use SELECT *.
INSERT liveTable
(col1, col2, col3)
SELECT col1, col2, col3
FROM dumpTable dt
WHERE NOT EXISTS
(
SELECT 1
FROM liveTable lt
WHERE lt.Id == dt.Id
)
Pro tip: You can also achieve the above by using an OUTER JOIN between the dump and live tables and using WHERE liveTable.col1 = NULL (you will probably need to qualify the column names selected with the dump table alias).
I figured out the issue.... my live table didn't have the ID field set as an identity, somehow when I created it that field wasn't set up correctly.
you can leave that column in your insert statment like this
insert into destination (col2, col3, col4)
select col2, col3 col4 from source
Don't do just
insert into destination
select * from source