View error in PostgreSQL - sql

I have a large query in a PostgreSQL database.
The Query is something like this:
SELECT * FROM table1, table2, ... WHERE table1.id = table2.id...
When I run this query as a sql query, the it returns the wanted row.
But when I tries to use the same query to create a view, it returns an error:
"error: column "id" specified more than once."
(I use pgAdminIII when executing the queries.)
I'll guess this happens because the resultset will have more than one column named "id". Is there someway to solve this, without writing all the column names in the query?

That happens because a view would have two id named columns, one from table1 and one from table2, because of the select *.
You need to specify which id you want in the view.
SELECT table1.id, column2, column3, ... FROM table1, table2
WHERE table1.id = table2.id
The query works because it can have equally named columns...
postgres=# select 1 as a, 2 as a;
a | a
---+---
1 | 2
(1 row)
postgres=# create view foobar as select 1 as a, 2 as a;
ERROR: column "a" duplicated
postgres=# create view foobar as select 1 as a, 2 as b;
CREATE VIEW

If only join columns are duplicated (i.e. have the same names), then you can get away with changing:
select *
from a, b
where a.id = b.id
to:
select *
from a join b using (id)

If you got here because you are trying to use a function like to_date and getting the "defined more than once" error, note that you need to use a column alias for functions, e.g.:
to_date(o.publication_date, 'DD/MM/YYYY') AS publication_date

No built-in way in the language to solve it (and frankly, * is a bad practice in general because it can cause latent defects to arise as the table schemas change - you can do table1.*, table2.acolumn, tabl2.bcolumn if you want all of one table and selectively from another), but if PostgreSQL supports INFORMATION_SCHEMA, you can do something like:
DECLARE #sql AS varchar
SELECT #sql = COALESCE(#sql + ', ', '')
+ '[' + TABLE_NAME + '].[' + COLUMN_NAME + ']'
+ CHAR(13) + CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN ('table1', 'table2')
ORDER BY TABLE_NAME, ORDINAL_POSITION
PRINT #sql
And paste the results in to save a lot of typing. You will need to manually alias the columns which have the same name, of course. You can also code-gen unique names if you like (but I don't):
SELECT #sql = COALESCE(#sql + ', ', '')
+ '[' + TABLE_NAME + '].[' + COLUMN_NAME + '] '
+ 'AS [' + TABLE_NAME + '_' + COLUMN_NAME + ']'
+ CHAR(13) + CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN ('table1', 'table2')
ORDER BY TABLE_NAME, ORDINAL_POSITION

Related

Iterate through databases with similar tables but slightly different column names

In SQL Server, I am trying to consolidate multiple databases with similar relational table structure into one database. Let's say each database contains a table called items and each items table has a column for the name of an item. However, this column name varies depending on the database we are in. Some databases may have Item_Name while others may have ItemName or Name, etc.
In the consolidated database, I am trying to append all of the items tables into one table, and I want to create a CASE expression that checks if a column name exists in the items table from any of the databases, and if it does, then to return the results from that table column.
This is what I have at the moment:
DECLARE #sql varchar(max)
SELECT #sql = #sql + 'UNION ALL
SELECT ''' + name + ''' AS DatabaseName,
CASE WHEN EXISTS (SELECT COLUMN_NAME
FROM ' + QUOTENAME(name) + '.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''items'' AND
COLUMN_NAME = ''Item_Name'')
THEN Item_Name
WHEN EXISTS (SELECT COLUMN_NAME
FROM ' + QUOTENAME(name) + '.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''items'' AND
COLUMN_NAME = ''ItemName'')
THEN ItemName
...
END AS NameOfItem
FROM ' + QUOTENAME(name) + 'dbo.Items'
FROM master.sys.databases
WHERE name <> 'master' ...
SET #sql = STUFF(#sql, 1, LEN(' UNION ALL'), '')
EXEC(#sql)
I believe this throws an error because of the CASE expression, since in the THEN clauses I am specifying a column that may not exist in that current table, even though it wouldn't satisfy the condition in the WHEN clause. How would I resolve this issue?

SQL Search for Data in Multiple Columns

Dears,
I have a table as shown below as a sample, and I want to run one query by which i can find all the yellow highlighted ones by using %AAA%.
Instead of running the Where command on each column one by one, I can do one general find option and it will list all the rows.
Thank you in advance!!
You can include all the conditions in one where clause using or:
where col1 like '%aaa%' or
col2 like '%aaa%' or
. . . -- and so on for all the columns
Unpivot the columns and do a WHERE based on that:
select *
from Table
where exists (select 1
from (values (col1), (col2), (col3) ) AS v (allCols) -- etc
where v.allCols like '%aaa%'
);
If you can't be bothered to type them out, try this little query:
select STRING_AGG('(' + c.name + ')', ', ')
from sys.columns c
where c.object_id = OBJECT_ID('Name_Of_Table_Here');
If you are using sql server then you can write dynamic query to do so. Please try below query:
declare #sql as varchar(max);
select #sql = 'select * from [TableName] where '
+ stuff((
select ' or [' + [column_name] + '] like ''%AAA%'''
from information_schema.columns
where table_name = 'TableName'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
This query will return every row in which at least one column contains AAA.
If you are using PostgreSQL, you can use its JSON functionality:
select t.*
from the_table t
where exists (select *
from jsonb_each(to_jsonb(t)) as x(col,val)
where val like '%AAA%');
If you are using Postgres 12 or later you can use a SQL/JSON path expression:
select t.*
from the_table t
where to_jsonb(t) ## '$.* like_regex "AAA" flag "i"'

SQL loop for each column in a table

Say I have a table called:
TableA
The following columns exist in the table are:
Column1, Column2, Column3
what I am trying to accomplish is to see how many records are not null.
to do this I have the following case statement:
sum(Case when Column1 is not null then 1 else 0 end)
What I want is the above case statement for every table that exists from a list provided and to be run for each columns that exists in the table.
So for the above example the case statment will run for Column1, Column2 and Column3 as there are 3 columns in that particular table etc
But I want to specfiy a list of tables to loop through executing the logic above
create procedure tab_cols (#tab nvarchar(255))
as
begin
declare #col_count nvarchar(max) = ''
,#col nvarchar(max) = ''
select #col_count += case ORDINAL_POSITION when 1 then '' else ',' end + 'count(' + QUOTENAME(COLUMN_NAME,']') + ') as ' + QUOTENAME(COLUMN_NAME,']')
,#col += case ORDINAL_POSITION when 1 then '' else ',' end + QUOTENAME(COLUMN_NAME,']')
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #tab
order by ORDINAL_POSITION
declare #stmt nvarchar(max) = 'select * from (select ' + #col_count + ' from ' + #tab + ') t unpivot (val for col in (' + #col + ')) u'
exec sp_executesql #stmt
end
Wouldn't it be easy as this?
SELECT AccountID
,SUM(Total) AS SumTotal
,SUM(Profit) AS SumProfit
,SUM(Loss) AS SumLoss
FROM tblAccount
GROUP BY AccountID
If I understand this correctly you want to get the sums, but not for all rows in one go but for each accountID separately. This is what GROUP BY is for...
If ever possible try to avoid loops, cursors and other procedural approaches...
UPDATE: Generic approach for different tables
With different tables you will - probably - need exactly the statement I show above, but you'll have to generate it dynamically and use EXEC to execute it. You can go through INFORMATION_SCHEMA.COLUMNS to get the columns names...
But:
How should this script know generically, which columns should be summed up? You might head for data_type like 'decimal%' or similar...
What about the other columns and their usage in GROUP BY?
How would you want to place aliases
How do you want to continue with a table of unknown structure?
To be honest: I think, there is no real-generic-one-for-all approach for this...

Inject array of columns to COUNT in SELECT MSSQL - dynamically

Currently I have prepared a sting of columns which can be added (hence the need for the dynamic query.
I have #cols which can be printed with an output like "Color","Size","Width"
I then have a SELECT/COUNT statement which needs to look like as follows...
SELECT
Product_code,
count(distinct [Color]),
count(distinct [Size]),
count(distinct [Width])
FROM.....
I need of the columns that I have in my string of columns to be counted with distinct..
Also would be even better if I could add a AS with the name of each of these in here too!
Many help is much appreciated - my SQL are OK but the dynamic bit turns me blue!
Cheers.
Convert your comma seperated list to a table first. See this
Assuming that table name to be ListOfColumns
DECLARE #Query VARCHAR( 1000 ) = '';
SELECT #Query+=#Query + ', COUNT(DISTINCT ' + COLUMN_NAME + ') AS ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN ListOfColumns d ON c.COLUMN_NAME = d.ColName
WHERE TABLE_NAME = 'MyTable';
SET #Query = 'SELECT ' + STUFF( #query,1,3,'' ) + ' FROM Tbl';
EXEC ( #Query );

How to insert into tempoary table twice

I've picked up some SQL similar to the following:
IF EXISTS(SELECT name FROM tempdb..sysobjects WHERE name Like N'#tmp%'
and id=object_id('tempdb..#tmp'))
DROP TABLE #tmp
into #tmp
select * from permTable
I need to add more data to #tmp before continuing processing:
insert into #tmp
select * from permTable2
But this gives errors because SQL has assumed sizes and types for #tmp columns (e.g. if permTable has a column full of ints but permTable2 has column with same name but with a NULL in one record you get "Cannot insert the value NULL into column 'IsPremium', table 'tempdb.dbo.#tmp").
How do I get #tmp to have the types I want? Is this really bad practise?
Have you considered creating a table var instead? You can declare the columns like such
declare #sometable table(
SomeField [nvarchar](15),
SomeOtherField [decimal](15,2));
This is why select into is a poor idea for your problem. Create the table structure specifically with a create table command and then write two insert statements.
It isn't possible.
If you need to generate the table definition typelist now,
create a view from the select statement, and read the columns and their definition from information_schema... (this work of art won't consider decimal and/or datetime2)
Note: this will give you the lowest possible field-length for varchar/varbinary columns you currently selected. You need to adjust them manually...
SELECT
','
+ COLUMN_NAME
+ ' '
+ DATA_TYPE
+ ' '
+ ISNULL
(
'('
+
CASE
WHEN CHARACTER_MAXIMUM_LENGTH = -1
THEN 'MAX'
ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS varchar(36))
END
+ ')'
, ''
)
+ ' '
+ CASE WHEN IS_NULLABLE = 'NO' THEN 'NOT NULL' ELSE '' END
FROM information_schema.columns
WHERE table_name = '________theF'
ORDER BY ORDINAL_POSITION
And the field-list for the insert-statement:
SELECT
',' + COLUMN_NAME
FROM information_schema.columns
WHERE table_name = '________theF'
ORDER BY ORDINAL_POSITION