Retrieve column descriptions from SQL Server-linked table in MS Access - sql-server

I am linking to tables in SQL Server from an MS Access front-end. There are column descriptions for some of the tables in SQL Server that I would like to bring forward when I create the linked tables in Access. Is there a way to get at the column descriptions programmatically?
(I know how to append the description to the linked tables, I just need help getting at the descriptions in the back end.)

Try something like:
DECLARE #TableName varchar(100)
SELECT #TableName = 'yourtablename'
-- This will determine if we're using version 9 (2005) of SQL Server, and execute code accordingly
IF CAST(REPLACE(SUBSTRING(CAST(SERVERPROPERTY('productversion') as varchar),1,2), '.','') as int) >= 9
BEGIN
-- This is a SQL 2005 machine
SELECT
[Table Name] = OBJECT_NAME(c.object_id),
[Column Name] = c.name,
[Description] = ex.value
FROM
sys.columns c
LEFT OUTER JOIN
sys.extended_properties ex
ON
ex.major_id = c.object_id
AND ex.minor_id = c.column_id
AND ex.name = 'MS_Description'
WHERE
OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0
AND OBJECT_NAME(c.object_id) = #TableName
ORDER
BY OBJECT_NAME(c.object_id), c.column_id
END
ELSE
BEGIN
-- assume this is a SQL 2000
SELECT
[Table Name] = i_s.TABLE_NAME,
[Column Name] = i_s.COLUMN_NAME,
[Description] = s.value
FROM
INFORMATION_SCHEMA.COLUMNS i_s
LEFT OUTER JOIN
sysproperties s
ON
s.id = OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME)
AND s.smallid = i_s.ORDINAL_POSITION
AND s.name = 'MS_Description'
WHERE
OBJECTPROPERTY(OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME), 'IsMsShipped')=0
AND i_s.TABLE_NAME = #TableName
ORDER BY
i_s.TABLE_NAME, i_s.ORDINAL_POSITION
END

Related

Run query for each database in a list and append results

I have the below code to pull the row and column counts from each table within a database (e.g., db1). But I have several databases (e.g., db1, db2 etc.) , so manually updating the database name in the USE statement for every run isn't very convenient. Is there a way to pass a list of database names in a cursor (or something else that allows iteration) and then run the below query for every database in the list, appending the results from each run? I can get the list of database names from this query select name from master.dbo.sysdatabases where name like '%db%'.
USE [db1]
;with [rowCount] as
(
SELECT DB_NAME() as [DB_Name],
QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) AS [TableName],
SUM(sPTN.Rows) AS [RowCount]
FROM SYS.OBJECTS AS sOBJ
INNER JOIN SYS.PARTITIONS AS sPTN
ON sOBJ.object_id = sPTN.object_id
WHERE
sOBJ.type = 'U'
AND sOBJ.is_ms_shipped = 0x0
AND index_id < 2 -- 0:Heap, 1:Clustered
GROUP BY
sOBJ.schema_id
,sOBJ.name
)
,columnCount as
(
select
QUOTENAME(col.TABLE_SCHEMA) + '.' + QUOTENAME(col.TABLE_NAME) AS [TableName],
count(*) as ColumnCount
from INFORMATION_SCHEMA.COLUMNS col
inner join INFORMATION_SCHEMA.TABLES tbl
on col.TABLE_SCHEMA = tbl.TABLE_SCHEMA
and col.TABLE_NAME = tbl.TABLE_NAME
and tbl.TABLE_TYPE <> 'view'
group by
QUOTENAME(col.TABLE_SCHEMA) + '.' + QUOTENAME(col.TABLE_NAME)
)
select r.[DB_Name], r.TableName, r.[RowCount], c.ColumnCount
from [rowCount] r
inner join columnCount c
on r.TableName = c.TableName
ORDER BY r.[TableName]

Need to change GUID to varchar or char

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

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 column stats in SQL Server 2008

I am trying to determine two things in a SQL Server 2008 database.
First, I need to know the columns that have null values in them.
Second, I need to know the count of nulls per column in the actual tables.
I know I can get the first one by doing:
SELECT t.name, c.name
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.is_nullable = 1
I am struggling to find the second part though.
Say you have a table atable with columns not_nullable and is_nullable. As count does not count anything - you could instead count only where the columns is null
SELECT count(*) count_nulls FROM atable WHERE is_nullable is null;
Now how to do that for all nullable columns?
I came up with this - but its veeeery slow. But then again - how often do you need to do that?
create table #nullcolumns(nullable_column varchar(255), count_nulls int);
declare #sqladd nvarchar(1000);
DECLARE users_cursor CURSOR FOR
SELECT /* TOP 20 */ 'INSERT INTO #nullcolumns SELECT '''+
t.name+'.'+c.name+''' as nullable_column, count(*) from '
+t.name+' WHERE '+c.name+' IS NULL '
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.is_nullable = 1
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO #sqladd
WHILE ##FETCH_STATUS = 0
BEGIN
exec sp_executesql #sqladd;
print #sqladd;
FETCH NEXT FROM users_cursor
INTO #sqladd
END
CLOSE users_cursor
DEALLOCATE users_cursor
SELECT * FROM #nullcolumns;

SQL Server: How to list all CLR functions/procedures/objects for assembly

Question: In SQL Server 2005, how can I list all SQL CLR-functions/procedures that use assembly xy (e.g. MyFirstUdp) ?
For example a function that lists HelloWorld for query parameter MyFirstUdp
CREATE PROCEDURE HelloWorld
AS EXTERNAL NAME MyFirstUdp.[SQL_CLRdll.MySQLclass].HelloWorld
GO
after I ran
CREATE ASSEMBLY MyFirstUdp FROM 'C:\Users\username\Documents\Visual Studio 2005\Projects\SQL_CLRdll\SQL_CLRdll\bin\Debug\SQL_CLRdll.dll
I can list all assemblies and all functions/procedures,
but I seem to be unable to associate the assembly to the functions/procedures...
Check out the sys.assembly_modules view:
select * from sys.assembly_modules
This should list all functions and the assemblies they're defined in. See the Books Online help page about it.
Returns one row for each function,
procedure or trigger that is defined
by a common language runtime (CLR)
assembly.
I use the following SQL:
SELECT so.name AS [ObjectName],
so.[type],
SCHEMA_NAME(so.[schema_id]) AS [SchemaName],
asmbly.name AS [AssemblyName],
asmbly.permission_set_desc,
am.assembly_class,
am.assembly_method
FROM sys.assembly_modules am
INNER JOIN sys.assemblies asmbly
ON asmbly.assembly_id = am.assembly_id
AND asmbly.is_user_defined = 1 -- if using SQL Server 2008 or newer
-- AND asmbly.name NOT LIKE 'Microsoft%' -- if using SQL Server 2005
INNER JOIN sys.objects so
ON so.[object_id] = am.[object_id]
UNION ALL
SELECT at.name AS [ObjectName],
'UDT' AS [type],
SCHEMA_NAME(at.[schema_id]) AS [SchemaName],
asmbly.name AS [AssemblyName],
asmbly.permission_set_desc,
at.assembly_class,
NULL AS [assembly_method]
FROM sys.assembly_types at
INNER JOIN sys.assemblies asmbly
ON asmbly.assembly_id = at.assembly_id
AND asmbly.is_user_defined = 1 -- if using SQL Server 2008 or newer
-- AND asmbly.name NOT LIKE 'Microsoft%' -- if using SQL Server 2005
ORDER BY [AssemblyName], [type], [ObjectName]
Please note:
User-Defined Types (UDTs) are found in: sys.assembly_types
You can only list SQLCLR references that have been used in CREATE { PROCEDURE | FUNCTION | AGGREGATE | TRIGGER | TYPE } statements. You cannot find SQLCLR methods that have not yet been referenced by a CREATE. Meaning, you cannot say: "give me a list of methods in this assembly that I can create T-SQL objects for".
For more info on working with SQLCLR in general, please visit: SQLCLR Info
Here is a generalization of srutzky's query (above) that goes through all DBs on a server using a cursor. Sorry about the formatting, but this is handy if you have to search through 500 DB's you've inherited.
set nocount on
declare #cmd nvarchar(4000)
declare curDBs cursor read_only for
SELECT name FROM MASTER.sys.sysdatabases
declare #NameDB nvarchar(100)
create table #tmpResults (
DatabaseName nvarchar(128)
, ObjectName nvarchar(128)
, ObjectType char(2)
, SchemaName nvarchar(128)
, AssemblyName nvarchar(128)
, PermissionSet nvarchar(60)
, AssemblyClass nvarchar(128)
, AssemblyMethod nvarchar(128));
open curDBs; while (1=1)
begin
fetch next from curDBs into #NameDB
if ##fetch_status <> 0 break
set #cmd = N'
USE [' + #NameDB + N'];
begin try
insert into #tmpResults
SELECT ''' + #NameDB + N''',
so.name AS [ObjectName],
so.[type],
SCHEMA_NAME(so.[schema_id]) AS [SchemaName],
asy.name AS [AssemblyName],
asy.permission_set_desc,
am.assembly_class,
am.assembly_method
FROM sys.assembly_modules am
INNER JOIN sys.assemblies asy
ON asy.assembly_id = am.assembly_id
AND asy.is_user_defined = 1
INNER JOIN sys.objects so
ON so.[object_id] = am.[object_id]
UNION ALL
SELECT ''' + #NameDB + N''',
at.name AS [ObjectName],
''UDT'' AS [type],
SCHEMA_NAME(at.[schema_id]) AS [SchemaName],
asy.name AS [AssemblyName],
asy.permission_set_desc,
at.assembly_class,
NULL AS [assembly_method]
FROM sys.assembly_types at
INNER JOIN sys.assemblies asy
ON asy.assembly_id = at.assembly_id
AND asy.is_user_defined = 1
ORDER BY [AssemblyName], [type], [ObjectName]
print ''' + #NameDB + N' ' + cast(##rowcount as nvarchar) + N'''
end try
begin catch
print ''Error processing ' + #NameDB + '''
end catch
'
--print #cmd
EXEC sp_executesql #cmd
end
close curDBs; deallocate curDBs
select * from #tmpResults
drop table #tmpResults
Here it a script found on sqlhint.com:
SELECT
SCHEMA_NAME(O.schema_id) AS [Schema], O.name,
A.name AS assembly_name, AM.assembly_class,
AM.assembly_method,
A.permission_set_desc,
O.[type_desc]
FROM
sys.assembly_modules AM
INNER JOIN sys.assemblies A ON A.assembly_id = AM.assembly_id
INNER JOIN sys.objects O ON O.object_id = AM.object_id
ORDER BY
A.name, AM.assembly_class
Also, you have the option to see all the places where that CLR object is used.
SELECT
modules.assembly_class AS AssemblyClass,
modules.assembly_method AS MethodName,
obj.type_desc AS MethodType,
files.name AS FilePath,
assemb.name AS AssemblyName,
assemb.clr_name,
assemb.create_date,
assemb.modify_date,
assemb.permission_set_desc
--,*
FROM
sys.assembly_modules AS modules
JOIN sys.assembly_files AS files ON files.assembly_id = modules.assembly_id
JOIN sys.assemblies AS assemb ON assemb.assembly_id = modules.assembly_id
JOIN sys.objects AS obj ON obj.object_id = modules.object_id
Or you can use
SELECT * FROM sys.dm_clr_appdomains;
which returns a list of assemblies and in what database they are stored.

Resources