Need to change GUID to varchar or char - sql-server

I am trying to use a reference table to update a code, but I am getting the error:
Conversion failed when converting from a character string to
uniqueidentifier
The ID is a guid: 086B9FE7-3980-47D7-BB05-003708F1D564 and the reference code I want to use is 4 characters, like H100.
I received the initial error when I tried to alter the datatype in the table and then update the file with the values from the reference table. I have tried converting and casting based on other articles, but even if I cast or convert successfully, I still get the same message.
RefTable:
Id | ReportCode
6340FCEA-161C-42F4-8D7F-46B4C2E6C4E2 | H100
DataTable:
CauseId
6340FCEA-161C-42F4-8D7F-46B4C2E6C4E2
Code I am using to try and update. The first works, and the second bring the error msg:
select cast(nvarchar(36), ID) as ID
from [dbo].[reftable]
UPDATE dbo.datatable
SET causeid = L.reportcode
FROM dbo.datatable S
join dbo.reftable L on S.causeid = L.id

Uniqueidentifier is a particular column type that holds 36 bytes, with some hyphen characters in the middle. Asigning a string value that doesn't match it's format will always fail when trying to convert it.
-- Conversion failed when converting from a character string to uniqueidentifier.
SELECT CONVERT(UNIQUEIDENTIFIER, 'H100')
-- OK
SELECT CONVERT(UNIQUEIDENTIFIER, 'c029f8be-29dc-41c1-8b38-737b4cc5a4df')
If you want to use a common VARCHAR as your new causeid value, you need to first convert the column type to VARCHAR (or NVARCHAR).
ALTER TABLE dbo.datatable ALTER COLUMN causeid VARCHAR(200) -- NOT NULL if you need
If you can't alter the data type it's probably because there is an INDEX or a CONSTRAINT linked to it. When you try to alter, the SQL engine will tell you which object is linked to it. You will have to drop them, alter the column type and then create them again.
I use these queries to check indexes and constraints. I updated the search values for your table.
DECLARE #table_name VARCHAR(200) = 'datatable'
DECLARE #column_name VARCHAR(200) = 'causeid'
-- Indexes
SELECT
SchemaName = SCHEMA_NAME(t.schema_id),
TableName = t.name,
IndexName = ind.name,
IndexType = CASE ind.index_id WHEN 0 THEN 'Heap' WHEN 1 THEN 'Clustered' ELSE 'Nonclustered' END,
Disabled = ind.is_disabled,
ColumnOrder = ic.index_column_id,
ColumnName = col.name,
ColumnType = y.name,
ColumnLength = y.max_length,
ColumnIncluded = ic.is_included_column
FROM
sys.indexes ind
INNER JOIN sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id
INNER JOIN sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id
INNER JOIN sys.tables t ON ind.object_id = t.object_id
INNER JOIN sys.types y ON y.user_type_id = col.user_type_id
WHERE
t.is_ms_shipped = 0 AND
t.name = #table_name AND
col.name = #column_name
ORDER BY
SchemaName,
t.name,
ind.name,
ic.index_column_id
-- Constraints
SELECT
TableName = t.Name,
ColumnName = c.Name,
dc.Name,
dc.definition
FROM
sys.tables t
INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id
INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND c.column_id = dc.parent_column_id
WHERE
t.name = #table_name AND
c.name = #column_name
ORDER BY
t.Name

Related

Check If column exists or not in table and rename them

I am trying to merge two different rules into one that should run in two databases server.
I did merged the files. but i am stuck at the this columns.
There are two columns with different names but the same data in the two databases. I want my query to check if if this column exist
First column - APE.RecProdOwner
Second Column - APE.ReconciliationOwner
I want my query to check for both these columns in the database and if one of the two exists then it should store the data into the new column name as “RecProdOwner”
You can use an EXISTS against the sys objects, and then simply use sys.sp_rename to rename the column:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable ([APE.RecProdOwner] int,
ReconciliationOwner int);
GO
IF EXISTS (SELECT 1
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE s.name = N'dbo'
AND t.name = N'YourTable'
AND c.name = N'APE.RecProdOwner')
EXEC sys.sp_rename N'dbo.YourTable.[APE.RecProdOwner]','RecProdOwner','COLUMN';
IF EXISTS (SELECT 1
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE s.name = N'dbo'
AND t.name = N'YourTable'
AND c.name = N'APE.ReconciliationOwner')
EXEC sys.sp_rename N'dbo.YourTable.[APE.ReconciliationOwner]','ReconciliationOwner','COLUMN';
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;

Using column names from another table to define the name of a new table

I'm looking to create a new table name as the name of the column in a previous table but it can't seem to recognize the #table_name_2 variable. I'm trying to do the following;
DECLARE #table_name_2 varchar(MAX)
SET #table_name_2 =
(select
col.name as column_name
from sys.tables as tab
inner join sys.columns as col
on tab.object_id = col.object_id
left join sys.types as t
on col.user_type_id = t.user_type_id
where schema_name(tab.schema_id) = 'Staging' and tab.name = 'QACalculator' and col.column_id = 2)
CREATE TABLE #table_name_2
(
MI_KEY NOT NULL
ISSUE NULL
)

Collation issue while join query in MVC5 and SQL Server 2012

When I join two tables I got a collation issue that is
System.Data.SqlClient.SqlException: Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AI" in the equal to operation.
Then I set the collation in my db using the following code
ALTER DATABASE [CAM] COLLATE SQL_Latin1_General_CP1_CI_AS;
ALTER TABLE CAM_Users
ALTER COLUMN [EmployeeCode] VARCHAR(50)
COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL;
But I still get the same error.
My join query is this:
List<DTOUserManagement> users = (from CAMuser in _unitOfWorkAsync.RepositoryAsync<CAM_Users>().Queryable()
join QRuser in _unitOfWorkAsync.RepositoryAsync<CAM_V_EmployeeMaster>().Queryable() on CAMuser.EmployeeCode equals QRuser.EmployeeCode into t
from t1 in t.DefaultIfEmpty()
join CAMDomain in _unitOfWorkAsync.RepositoryAsync<CAM_Domain>().Queryable() on CAMuser.DomainID equals CAMDomain.DomainID into t2
from t3 in t2.DefaultIfEmpty()
where CAMuser.IsActive
select new DTOUserManagement
{
TransactUserCode = CAMuser.TransactUserCode,
EmployeeCode = CAMuser.EmployeeCode,
EmployeeName = t1.EmployeeName,
Email = t1.EMail,
DomainID = CAMuser.DomainID,
DomainName = t3.DomainName,
IsActive = CAMuser.IsActive,
AssignedRole = CAMuser.AssignedRoles
}).ToList();
How can I solve this?
Please reply anybody
The problem here is that the COLLATION must match on joining columns.
There are two ways to fix this. First you could change the collation on each column. Or second, you can change the collation at execution time. Here's an example of the second approach:
Sample Data
/* T1 and T2 are identical tables in structre and content, except
* for the collation.
*/
DECLARE #T1 TABLE
(
ID VARCHAR(3) COLLATE SQL_Latin1_General_CP1_CI_AS
)
;
DECLARE #T2 TABLE
(
ID VARCHAR(3) COLLATE Latin1_General_CI_AI
)
;
INSERT INTO #T1
(
ID
)
VALUES
('x'),
('y'),
('z')
;
INSERT INTO #T2
(
ID
)
VALUES
('x'),
('y'),
('z')
;
Anti Pattern - Will not Work
/* This query will failed with the error:
* Cannot resolve the collation conflict between "Latin1_General_CI_AI" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID
;
Corrected - No Error
/* Success.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID COLLATE Latin1_General_CI_AI
;
You need to check your tables that are involved in these JOINs here to verify that all columns involved have the same collation.
You can do this with this SQL query:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
t.name IN ('CAM_Users', 'CAM_Domain') -- add any further tables to check
AND ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
ORDER BY
t.name, c.name
If there are columns that do not match the database default collation, you need to change those to be the same as all other columns. Once all the string columns in those tables are the same collation, then your joins should work.
Update: use this query to find those tables & columns that do not have the current default database collation:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
AND c.collation_name <> 'SQL_Latin1_General_CP1_CI_AS'
ORDER BY
t.name, c.name

Get total size for a list of tables

I have a prod database that is very large in size. I need to copy this DB to make a test environment, however there is not enough disk space.
But actually there is no need to copy rows from tables containing user-data, only those tables that describe functionality are necessary. For tables with user-data I only need to copy table "description" (columns, indexes, triggers, ...).
How (with what query) can I estimate the size of DB without data from some tables?
SET NOCOUNT ON
DBCC UPDATEUSAGE(0)
-- DB size.
EXEC sp_spaceused
-- Table row counts and sizes.
CREATE TABLE #t
(
[name] NVARCHAR(128),
[rows] CHAR(11),
reserved VARCHAR(18),
data VARCHAR(18),
index_size VARCHAR(18),
unused VARCHAR(18)
)
INSERT #t EXEC sp_msForEachTable 'EXEC sp_spaceused ''?'''
SELECT *
FROM #t
-- # of rows.
SELECT SUM(CAST([rows] AS int)) AS [rows]
FROM #t
DROP TABLE #t
*by Alexander Groß *
Link to source
SELECT
schema_qualified_table = s.name + '.' + t.name,
KB = SUM(ps.reserved_page_count) * 8192/1024.0
FROM sys.tables AS t
INNER JOIN sys.schemas AS s ON t.[schema_id] = s.[schema_id]
INNER JOIN sys.dm_db_partition_stats AS ps
ON t.[object_id] = ps.[object_id]
WHERE s.name + '.' + t.name IN (N'dbo.table1', N'dbo.table2', ...)
-- list of tables you do care about ^^^^^^^^^^ ^^^^^^^^^^
GROUP BY s.name + '.' + t.name;
A little tidier might be:
;WITH x AS
(
SELECT o = t.[object_id], n = s.name + '.' + t.name
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
)
SELECT
schema_qualified_table = x.n,
KB = SUM(ps.reserved_page_count) * 8192/1024.0
FROM x INNER JOIN sys.dm_db_partition_stats AS ps
ON x.o = ps.[object_id]
WHERE x.n IN (N'dbo.table1', N'dbo.table2', ...);
Posting my expansion to Aaron Bertrand's answer, just for readability. Only difference is adding the join to the SCHEMAS table, and including schema name in the output and group by.
SELECT
s.name,
t.name,
KB = SUM(ps.reserved_page_count) * 8192/1024.0
FROM sys.tables AS t
INNER JOIN sys.dm_db_partition_stats AS ps
ON t.[object_id] = ps.[object_id]
INNER JOIN sys.schemas S
ON t.schema_id = s.schema_id
WHERE t.name IN (<YOUR TABLES GO HERE) -- list of tables you do care about
GROUP BY t.name,
s.name;
As Aaron has rightly pointed out, trying to filter on a combination of tables and schemas could get ugly very fast. You could easily end up with a huge collection of ORs:
(S.SCHEMA = 'SCHEMA1' and T.NAME = 'FOO')
OR
(S.SCHEMA = 'SCHEMA2' and T.NAME = 'FOO')
OR
(S.SCHEMA = 'SCHEMA1' and T.name = 'BAR')
...

How to get list of child tables for a database table?

I have to write a delete script to delete rows form a database table. However the table has a lot of children tables (foreign keys) and those children tables have children tables too.
There are foreign keys for all relationships and I'd like to use this info to get the list of tables where I'll have to deletes, in the correct order (leaf tables first and then up the dependency graph).
How can I get the list of child tables for a given table in the correct order?
try this on your database, this script will only give you the graph for one table at a time. I assume you have an Employee table but you would have to change line 2 to check a specific table of your database:
DECLARE #masterTableName varchar(1000)
SET #masterTableName = 'Employee'
DECLARE #ScannedTables TABLE( Level int, Name varchar(1000) collate Latin1_General_CI_AS )
DECLARE #currentTableCount INT
DECLARE #previousTableCount INT
DECLARE #level INT
SET #currentTableCount = 0
SET #previousTableCount = -1
SET #level = 0
INSERT INTO #ScannedTables VALUES ( #level, #masterTableName )
WHILE #previousTableCount <> #currentTableCount
BEGIN
SET #previousTableCount = #currentTableCount
INSERT INTO #ScannedTables
SELECT DISTINCT
#level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name
WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN ( SELECT Name FROM #ScannedTables WHERE Level = #level )
AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN ( SELECT Name FROM #ScannedTables )
SET #level = #level + 1
SELECT #currentTableCount = COUNT(*) FROM #ScannedTables
END
SELECT * FROM #ScannedTables
There is no simple generic answer for this, since tables can recursively depend on other tables including self relationships, etc. Your result could be more than simple tree.
Your best way should depend on your db model: if you have tree tables connected, then delete your data from third table first, than second, than third.
...or disable constraints, delete data, enable constraints.
...or change foreign keys to DELETE CASCADE.
It depends on your data model.
This article gives a good idea of how to do what you're asking.
EDIT: I've modified the original query given in the link to:
Make the script schema aware
Correct the bug noted in the
comments below
Not sure why the editor is doing such a poor job of formatting the code block.
with Fkeys as (
select distinct
OnTable = onTableSchema.name + '.' + OnTable.name
,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name
from
sysforeignkeys fk
inner join sys.objects onTable
on fk.fkeyid = onTable.object_id
inner join sys.objects againstTable
on fk.rkeyid = againstTable.object_id
inner join sys.schemas onTableSchema
on onTable.schema_id = onTableSchema.schema_id
inner join sys.schemas againstTableSchema
on againstTable.schema_id = againstTableSchema.schema_id
where 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name
)
,MyData as (
select
OnTable = s.name + '.' + o.name
,AgainstTable = FKeys.againstTable
from
sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join FKeys
on s.name + '.' + o.name = FKeys.onTable
left join Fkeys fk2
on s.name + '.' + o.name = fk2.AgainstTable
and fk2.OnTable = Fkeys.AgainstTable
where 1=1
and o.type = 'U'
and o.name not like 'sys%'
and fk2.OnTable is null
)
,MyRecursion as (
-- base case
select
TableName = OnTable
,Lvl = 1
from
MyData
where 1=1
and AgainstTable is null
-- recursive case
union all select
TableName = OnTable
,Lvl = r.Lvl + 1
from
MyData d
inner join MyRecursion r
on d.AgainstTable = r.TableName
)
select
Lvl = max(Lvl)
,TableName
,strSql = 'delete from [' + tablename + ']'
from
MyRecursion
group by
TableName
order by
1 desc
,2 desc

Resources