How to insert into tempoary table twice - sql

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

Related

Update columns in multiple tables by names pulled from a temporary table

I have a temp table where various table names and connected column names are stored. If I were to run a simple SELECT on it the results would look something like this:
----------------
TableName | ColumnName
------------------
Users | RoleId
Tables | OwnerId
Chairs | MakerId
etc...
I'm looking for a way to set mentioned column values in the connected tables to NULL.
I know how to do it via a CURSOR or a WHILE loop by processing each row individually but I'm trying to eliminate these performance hoarders from my stored procedures.
Is there any way to build a join by table names from the TableName column to the actual tables to then set connected ColumnName column values to NULL ?
Check this Script-
IF OBJECT_ID('SampleTable') IS NOT NULL
DROP TABLE SampleTable
CREATE TABLE SampleTable
(
Table_Name VARCHAR(50) NOT NULL,
Column_Name VARCHAR(50) NOT NULL
)
GO
INSERT INTO SampleTable
VALUES
('Users','RoleId'),('Tables','OwnerId'),('Chairs','MakerId') --Give your Combo here
GO
--Check this scripts
SELECT 'UPDATE ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(S1.TABLE_NAME) +
' SET ' + QUOTENAME(S1.COLUMN_NAME) + ' = NULL ; '
AS [Dynamic_Scripts]
FROM SampleTable S JOIN INFORMATION_SCHEMA.COLUMNS S1 ON s.Table_Name=s1.Table_Name and s.Column_Name=s1.Column_Name
--Check this scripts (multiple column single script; 1 table 'n' column - 1 update query)
SELECT 'UPDATE ' + CONCAT('[',TABLE_SCHEMA,'].[',S1.TABLE_NAME,'] SET ') + STRING_AGG(CONCAT('[',S1.COLUMN_NAME,']=NULL'),',') + ' ; ' AS [Dynamic_Scripts]
FROM SampleTable S JOIN INFORMATION_SCHEMA.COLUMNS S1 ON s.Table_Name=s1.Table_Name and s.Column_Name=s1.Column_Name
GROUP BY CONCAT('[',TABLE_SCHEMA,'].[',S1.TABLE_NAME,'] SET ')
Try this,
IF OBJECT_ID('SampleTable') IS NOT NULL
DROP TABLE SampleTable
CREATE TABLE SampleTable
(
Table_Name VARCHAR(50) NOT NULL,
Column_Name VARCHAR(50) NOT NULL
)
GO
INSERT INTO SampleTable
VALUES
('Users','RoleId'),('Tables','OwnerId'),('Chairs','MakerId')
,('Users','Appid'),('Tables','Column') --Give your Combo here
GO
declare #Sql nvarchar(1000)=''
;with CTE as
(
select QUOTENAME(a.Table_Name)Table_Name
,stuff((select ','+QUOTENAME(Column_Name),'=null'
from SampleTable B
where a.Table_Name=b.Table_Name for xml path('') ),1,1,'')UpdateCol
from SampleTable A
group by a.Table_Name
)
select #Sql=coalesce(#Sql+char(13)+char(10)+SingleUpdate,SingleUpdate)
from
(
select CONCAT('Update ',Table_Name,' ','SET ',UpdateCol)SingleUpdate
from cte
)t4
print #Sql
select #Sql
Execute sp_executeSql #Sql

One view from two tables with identical column names

We have two tables that we need to merge into a singular view. Normally I'd individually select columns to avoid this issue, however in this case the two tables are a combined 800 columns.
The only identical columns are the identifier columns. Unfortunately these cannot be changed as they are used by a 3rd party tool to sync table
Table A
GUID
Name
Address
...
Table B
GUID
Cell
Fax
Home2
...
Are good examples, just assume each table has 400 odd columns.
Obviously the traditional
SELECT a.*, b.* from table_a a, table_b a where a.guid = b.guid
Fails miserably. Is there any easy way to create the view without having to list out 799 individual column names? I was thinking perhaps a one off function to create the view but so far I'm hitting a wall.
You can use dynamic sql as a solution.
CREATE TABLE test1 (id INT, col1 NVARCHAR(50), col2 NVARCHAR(50))
GO
CREATE TABLE test2(id INT, col1 NVARCHAR(50), col2 NVARCHAR(50))
GO
DECLARE #sql NVARCHAR(max) = ''
; WITH cte AS (
SELECT
CASE WHEN TABLE_NAME = 'test1' THEN TABLE_NAME + '.' + COLUMN_NAME + ' AS ' + + COLUMN_NAME + 't1' ELSE TABLE_NAME + '.' + COLUMN_NAME + ' AS ' + + COLUMN_NAME + 't2' END AS a, 1 AS ID
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN ('test1', 'test2')
)
SELECT #sql =
'CREATE VIEW myview as
select ' + (
SELECT
STUFF(
(
SELECT ', '+ [A]
FROM cte
WHERE ID = results.ID
FOR XML PATH(''), TYPE
).value('(./text())[1]','VARCHAR(MAX)')
,1,2,''
) AS NameValues
FROM cte results
GROUP BY ID
) + ' from test1 join test2 on test1.id = test2.id'
PRINT #sql
--EXEC (#sql)
The result is
CREATE VIEW myview
AS
SELECT test1.id AS idt1 ,
test1.col1 AS col1t1 ,
test1.col2 AS col2t1 ,
test2.id AS idt2 ,
test2.col1 AS col1t2 ,
test2.col2 AS col2t2
FROM test1
JOIN test2 ON test1.id = test2.id

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...

how to find "String or binary data would be truncated" error on sql in a big query

I have a huge INSERT INTO TABLE1 (....) SELECT .... FROM TABLE2 statement. It gives me the error
"String or binary data would be truncated".
I know that one of the columns from TABLE2 is way bigger for one column from TABLE1 in the INSERT statement.
I have more than 100 columns in each table. So it is hard to find out the problem. Is there any easier way to figure this out?
You can query Information_Schema.Columns for both tables and check the difference in content length.
Assuming your tables have the same column names, you can use this:
SELECT t1.Table_Name, t1.Column_Name
FROM INFORMATION_SCHEMA.Columns t1
INNER JOIN INFORMATION_SCHEMA.Columns t2 ON (t1.Column_Name = t2.Column_Name)
WHERE t1.Table_Name = 'Table1'
AND t2.Table_Name = 'Table2'
AND ISNULL(t1.Character_maximum_length, 0) < ISNULL(t2.Character_maximum_length, 0)
Assuming your tables have different column names, you can do this and just look for the difference
SELECT Table_Name, Column_Name, Character_maximum_length
FROM INFORMATION_SCHEMA.Columns
WHERE Table_Name IN('Table1', 'Table2')
ORDER BY Column_Name, Character_maximum_length, Table_Name
To figure out which column the data is too long fit in, I would use following statement to output the results to a temp table.
SELECT ...
INTO MyTempTable
FROM Table2
Then use the query example from this article to get the max data length of each column. I have attached a copy of the code below.
DECLARE #TableName sysname = 'MyTempTable', #TableSchema sysname = 'dbo'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = STUFF((SELECT
' UNION ALL select ' +
QUOTENAME(Table_Name,'''') + ' AS TableName, ' +
QUOTENAME(Column_Name,'''') + ' AS ColumnName, ' +
CASE WHEN DATA_TYPE IN ('XML','HierarchyID','Geometry','Geography','text','ntext') THEN 'MAX(DATALENGTH('
ELSE 'MAX(LEN('
END + QUOTENAME(Column_Name) + ')) AS MaxLength, ' +
QUOTENAME(C.DATA_TYPE,'''') + ' AS DataType, ' +
CAST(COALESCE(C.CHARACTER_MAXIMUM_LENGTH, C.NUMERIC_SCALE,0) AS VARCHAR(10)) + ' AS DataWidth ' +
'FROM ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(Table_Name)
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE TABLE_NAME = #TableName
AND table_schema = #TableSchema
--AND DATA_TYPE NOT IN ('XML','HierarchyID','Geometry','Geography')
ORDER BY COLUMN_NAME
FOR XML PATH(''),Type).value('.','varchar(max)'),1,11,'')
EXECUTE (#SQL)
#ZoharPeled answer is great, but for temp tables you have to do something a little different:
SELECT t1.Table_Name
,t1.Column_Name
,t1.Character_maximum_length AS Table1_Character_maximum_length
,t2.Character_maximum_length AS Table2_Character_maximum_length
FROM INFORMATION_SCHEMA.Columns t1
INNER JOIN tempdb.INFORMATION_SCHEMA.COLUMNS t2 ON (t1.Column_Name = t2.Column_Name)
WHERE t1.Table_Name = 'Table1'
AND t2.Table_Name LIKE '#Table2%' -- Don't remove the '%', it's required
AND ISNULL(t1.Character_maximum_length, 0) < ISNULL(t2.Character_maximum_length, 0)
If the column names are the same, you could try something like this:
SELECT
c1.name as ColumnName,
c1.max_length AS Table1MaxLength,
c2.max_length AS Table2MaxLength
FROM
sys.columns c1
inner join sys.columns c2 on c2.name = c1.name
WHERE
c1.object_id = OBJECT_ID('TABLE1')
c2.object_id = OBJECT_ID('TABLE2')
You can query for the definitions of the two tables from information_schema.columns and then get the diff using EXCEPT
CREATE TABLE peter(a INT, b BIGINT, c VARCHAR(100));
CREATE TABLE peter2(a INT, b BIGINT, c VARCHAR(800));
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'peter'
EXCEPT
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'peter2'
Merhaba Arif,
What I can suggest is to make comparison easier is to list the related table column definitions from sys.columns and make the comparison manually
SELECT * FROM sys.columns WHERE object_id = object_id('tablename')
Perhaps you can limit the returned list with string data type columns, or numeric values with sizes like int, bigint, etc.
Try:
Select ID from TABLE2 where LEN(YourColumn) > SIZE
Try this one
With Data as (
SELECT
Table_Name, Column_Name, Character_maximum_length, Ordinal_Position,
LEAD(Character_maximum_length,1) Over(Partition by Column_Name Order by Table_Name) as NextValue
FROM INFORMATION_SCHEMA.Columns
WHERE Table_Name IN('Table1', 'Table2')
)
Select * , CHARACTER_MAXIMUM_LENGTH - NextValue as Variance
from Data
Where NextValue is not null and ( CHARACTER_MAXIMUM_LENGTH - NextValue) <> 0
ORDER BY Column_Name, Character_maximum_length, Table_Name
See #aaron-bertrand's researched response on Stack Exchange's DBA site.
Basically
Turn on the tracing with DBCC TRACEON(460);
Execute your INSERT code
Turn on the tracing with DBCC TRACEOFF(460);
Using Aaron's example code:
DBCC TRACEON(460);
GO
INSERT dbo.x(a) VALUES('foo');
GO
-- Drop if this is a test table with: DROP TABLE dbo.x;
DBCC TRACEOFF(460);
I am a beginner and I faced it during inserting names of Employees fname and lname. I did not specify the number of characters.
instead of writing this (wrong code):
create table Employee(
fname varchar ,
lname varchar
)
write this:
create table Employee(
fname varchar(10) ,
lname varchar(10)
)

View error in PostgreSQL

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