Application: MS SSMS version: 18.12.1
Windows 10
I have Table_Name1 that contains the below info
col1Name
col2Name
col3Name
-,99
fred
1,23
col1 - col2 - col3 are all string datatype
col1 ordinal value = 1
col2 ordinal value = 2
col3 ordinal value = 3
I used the below code to retrieve the columns names from TABLE_NAME1 that were related to monetary values and placed those columns into TABLE_NAME2
DROP TABLE IF EXISTS STAGE.DBO.TABLE_NAME2<BR>
SELECT SC.NAME AS COLUMN_NAME<BR>
,SC.COLORDER AS COLUMN_ORDINAL<BR>
,CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS SMALLINT) AS ROW_NUM<BR>
INTO STAGE.DBO.TABLE_NAME2<BR>
FROM SYSOBJECTS AS SO<BR>
INNER JOIN SYSCOLUMNS AS SC ON SO.ID = SC.ID<BR>
WHERE SO.XTYPE = 'U'<BR>
AND SO.NAME = 'TABLE_NAME2'<BR>
AND SC.COLORDER IN (1,3)<BR>
ORDER BY SC.COLORDER, SC.NAME<BR>
TABLE_NAME2 contains the following info
column_name
column_ordinal
row_num
col1Name
1
1
col2Name
3
2
What I want to do: loop through TABLE_NAME1 column names and if there is a match with a value in TABLE_NAME2, then return that column name and assign it to a variable. Use the variable in an UPDATE TABLE statement to replace the comma (,) with a dot (.)
Below is what i tried: to have the result assigned to a variable
DECLARE #TABLE_NAME1 NVARCHAR(MAX)
DECLARE #TABLE_NAME2 NVARCHAR(MAX)
DECLARE #SQLQRY1 NVARCHAR(MAX)
DECLARE #LOOP1 SMALLINT
DECLARE #TABLE_ROW_COUNT1 SMALLINT
DECLARE #XTYPE1 CHAR(1)
DECLARE #COL_NAME1 NVARCHAR(MAX)
SET #TABLE_NAME1 = 'CH_IBRO_HOUSEHOLD_ACTIVITY_STG'
SET #TABLE_NAME2 = 'STAGE.DBO.CH_IBRO_HH_ACT_COL_NAME_STG'
SET #LOOP1 = 1
SET #XTYPE1 = 'U'
SET #TABLE_ROW_COUNT1 =2
WHILE #LOOP1 <= #TABLE_ROW_COUNT1
BEGIN
SET #SQLQRY1 = N'SELECT #COL_NAME1=TBL2.COLUMN_NAME
FROM ( SELECT SC.[NAME] AS COL_NAME, SC.COLORDER AS COL_ORDER
FROM SYSOBJECTS AS SO
INNER JOIN SYSCOLUMNS AS SC ON SO.ID = SC.ID
WHERE SO.XTYPE = '''+ #XTYPE1 +'''
AND SO.NAME = '''+ #TABLE_NAME1 +'''
) AS TBL1
INNER JOIN ( SELECT *
FROM '+ #TABLE_NAME2 +'
) AS TBL2 ON TBL2.COLUMN_NAME = TBL1.COL_NAME
AND TBL2.COLUMN_ORDINAL = TBL1.COL_ORDER
WHERE TBL2.ROW_NUM = ' + CAST(#LOOP1 AS NVARCHAR(MAX))
EXECUTE #COL_NAME1 = sp_executesql #SQLQRY1 OUTPUT
PRINT 'COL_NAME = '+ CAST(#COL_NAME1 AS VARCHAR);
PRINT 'LOOP NUMBER = '+ CAST(#LOOP1 AS VARCHAR);
/* INCREMENT */
SET #LOOP1 = #LOOP1 + 1
END
Thank you all for your support as I am new to T-SQL
thank you for the documentation. It helped alot. Just wanted to post most solution.
STEP1: CREATE TEMPORARY TABLE THAT WILL CONTIAN THE NAME OF THE COLUMNS THAT ARE SUPPOSE TO HAVE NUMERIC DATA
USE [STAGE]
DROP TABLE IF EXISTS STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_COLUMN_NAME_STG
SELECT SC.NAME AS COLUMN_NAME
,SC.COLORDER AS COLUMN_ORDINAL
,CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS SMALLINT) AS ROW_NUM
INTO STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_COLUMN_NAME_STG
FROM SYSOBJECTS AS SO
INNER JOIN SYSCOLUMNS AS SC ON SO.ID = SC.ID
WHERE SO.XTYPE = 'U'
AND SO.NAME = 'CH_IBRO_HOUSEHOLD_ACTIVITY_STG'
AND SC.COLORDER IN (26,27,28,29,30,31,32,33,34,35,36,39,40,45,46,47,48,49,50,54,55,56,59,60,61,64,65,66,83,84)
ORDER BY SC.COLORDER, SC.NAME
STEP2: APPLY GLOBAL SCRUBBING LOGIC TO THE COLUMNS CONTIAN NUMERIC DATA
USE [STAGE]
DECLARE #TABLE_NAME1 NVARCHAR(MAX)
DECLARE #TABLE_NAME2 NVARCHAR(MAX)
DECLARE #SQLQRY1 NVARCHAR(MAX)
DECLARE #LOOP1 SMALLINT
DECLARE #TABLE_ROW_COUNT1 SMALLINT
DECLARE #XTYPE1 CHAR(1)
DECLARE #COL_NAME1 NVARCHAR(MAX)
SET #TABLE_NAME1 = 'CH_IBRO_HOUSEHOLD_ACTIVITY_STG'
SET #TABLE_NAME2 = 'STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_COLUMN_NAME_STG'
SET #LOOP1 = 1
SET #XTYPE1 = 'U'
SET #TABLE_ROW_COUNT1 = (SELECT COUNT(*) FROM STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_COLUMN_NAME_STG)
DECLARE #ParmDefinition nvarchar(500);
SELECT #ParmDefinition = N'#COL_NAME1 NVARCHAR(MAX) OUTPUT';
WHILE #LOOP1 <= #TABLE_ROW_COUNT1
BEGIN
SET #SQLQRY1 = N'SELECT #COL_NAME1=TBL2.COLUMN_NAME
FROM ( SELECT SC.[NAME] AS COL_NAME, SC.COLORDER AS COL_ORDER
FROM SYSOBJECTS AS SO
INNER JOIN SYSCOLUMNS AS SC ON SO.ID = SC.ID
WHERE SO.XTYPE = '''+ #XTYPE1 +'''
AND SO.NAME = '''+ #TABLE_NAME1 +'''
) AS TBL1
INNER JOIN ( SELECT *
FROM '+ #TABLE_NAME2 +'
) AS TBL2 ON TBL2.COLUMN_NAME = TBL1.COL_NAME
AND TBL2.COLUMN_ORDINAL = TBL1.COL_ORDER
WHERE TBL2.ROW_NUM = ' + CAST(#LOOP1 AS NVARCHAR(MAX))
EXEC sp_executesql #SQLQRY1, #ParmDefinition, #COL_NAME1 OUTPUT
PRINT 'COL_NAME = '+ CAST(#COL_NAME1 AS VARCHAR);
PRINT 'LOOP NUMBER = '+ CAST(#LOOP1 AS VARCHAR);
/* REMOVE ALL LEADING AND TRAILING SPACES */
UPDATE STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_STG
SET #COL_NAME1 = LTRIM(RTRIM(#COL_NAME1))
/* REMOVE ALL ASCII CHAR (13) PRESS ENTER */
UPDATE STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_STG
SET #COL_NAME1 = REPLACE(#COL_NAME1,CHAR(13),'')
/* REMOVE ALL ASCII CHAR (10) LINE FEED */
UPDATE STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_STG
SET #COL_NAME1 = REPLACE(#COL_NAME1,CHAR(10),'')
/* REMOVE ALL ASCII CHAR (9) HORIZONTAL TAB */
UPDATE STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_STG
SET #COL_NAME1 = REPLACE(#COL_NAME1,CHAR(9),'')
/* REMOVE ALL LEADING AND TRAILING SPACES - 2ND PASS */
UPDATE STAGE.DBO.CH_IBRO_HOUSEHOLD_ACTIVITY_STG
SET #COL_NAME1 = LTRIM(RTRIM(#COL_NAME1))
/* INCREMENT */
SET #LOOP1 = #LOOP1 + 1
END
Context: I am exploring a new database (in MS SQL server), and I want to know for each table, all columns that have null values.
I.e. result would look something like this:
table column nulls
Tbl1 Col1 8
I have found this code here on stackoverflow, that makes a table of table-columnnames - without the WHERE statement which is my addition.
I tried to filter for nulls in WHERE statement, but then the table ends up empty, and I see why - i am checking if the col name is actually null, and not its contents. But can't figure out how to proceed.
select schema_name(tab.schema_id) as schema_name,
tab.name as table_name,
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
-- in this where statement, I am trying to filter for nulls, but i get an empty result. and i know there are nulls
where col.name is null
order by schema_name, table_name, column_id
I also tried this (see 4th line):
select schema_name(tab.schema_id) as schema_name,
tab.name as table_name,
col.name as column_name
,(select count(*) from tab.name where col.name is null) as countnulls
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
order by schema_name, table_name, column_id
the last one returns an error "Invalid object name 'tab.name'."
column name can't be null but if you mean nullable column (column that accept null value) that has null value at least so you can use following statement:
declare #schema varchar(255), #table varchar(255), #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT schema_name(tab.schema_id) as schema_name,tab.name , col.name from sys.tables as tab
inner join sys.columns as col on tab.object_id = col.object_id
where col.is_nullable =1
order by schema_name(tab.schema_id),tab.name,col.name
OPEN getinfo
FETCH NEXT FROM getinfo into #schema,#table,#col
WHILE ##FETCH_STATUS = 0
BEGIN
set #schema = QUOTENAME(#schema)
set #table = QUOTENAME(#table)
set #col = QUOTENAME(#col)
SELECT #cmd = 'IF EXISTS (SELECT 1 FROM '+ #schema +'.'+ #table +' WHERE ' + #col + ' IS NULL) BEGIN SELECT '''+#schema+''' as schemaName, '''+#table+''' as tablename, '''+#col+''' as columnName, * FROM '+ #schema +'.'+ #table +' WHERE ' + #col + ' IS NULL end'
EXEC(#cmd)
FETCH NEXT FROM getinfo into #schema,#table,#col
END
CLOSE getinfo
DEALLOCATE getinfo
that use cursor on all nullable columns in every table in the Database then check if this column has at least one null value if yes will select schema Name, table name, column name and all records that has null value in this column
but if you want to get only count of nulls you can use the following statement:
declare #schema varchar(255), #table varchar(255), #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT schema_name(tab.schema_id) as schema_name,tab.name , col.name from sys.tables as tab
inner join sys.columns as col on tab.object_id = col.object_id
where col.is_nullable =1
order by schema_name(tab.schema_id),tab.name,col.name
OPEN getinfo
FETCH NEXT FROM getinfo into #schema,#table,#col
WHILE ##FETCH_STATUS = 0
BEGIN
set #schema = QUOTENAME(#schema)
set #table = QUOTENAME(#table)
set #col = QUOTENAME(#col)
SELECT #cmd = 'IF EXISTS (SELECT 1 FROM '+ #schema +'.'+ #table +' WHERE ' + #col + ' IS NULL) BEGIN SELECT '''+#schema+''' as schemaName, '''+#table+''' as tablename, '''+#col+''' as columnName, count(*) as nulls FROM '+ #schema +'.'+ #table +' WHERE ' + #col + ' IS NULL end'
EXEC(#cmd)
FETCH NEXT FROM getinfo into #schema,#table,#col
END
that use cursor on all nullable columns in every table in the Database then check if this column has at least one null value if yes will select schema Name, table name, column name and count all records that has null value in this column
I have a scenario where almost all of the tables have issues with the PK value as follows. This results is a database error or the violation of the PK insert.
When using the DBCC CheckIdent it displays an inconsistency between the next value and the current one.
Can anyone have a reason for the mismatch happening on several tables?
Since this database is then replicate, I'm afraid this error will propagate across the environment.
I adapted this script to fix it, but really trying to figure out the root of the problem.
/** Version 3.0 **/
if object_id('tempdb..#temp') is not null
drop table #temp
;
with cte as (
SELECT
distinct
A.TABLE_CATALOG AS CATALOG,
A.TABLE_SCHEMA AS "SCHEMA",
A.TABLE_NAME AS "TABLE",
B.COLUMN_NAME AS "COLUMN",
IDENT_SEED (A.TABLE_NAME) AS Seed,
IDENT_INCR (A.TABLE_NAME) AS Increment,
IDENT_CURRENT (A.TABLE_NAME) AS Curr_Value
, DBPS.row_count AS NumberOfRows
FROM INFORMATION_SCHEMA.TABLES A
inner join INFORMATION_SCHEMA.COLUMNS B on b.TABLE_NAME = a.TABLE_NAME and b.TABLE_SCHEMA = a.TABLE_SCHEMA
inner join sys.identity_columns IC on OBJECT_NAME (IC.object_id) = a.TABLE_NAME
inner join sys.dm_db_partition_stats DBPS ON DBPS.object_id =IC.object_id
inner join sys.indexes as IDX ON DBPS.index_id =IDX.index_id
WHERE A.TABLE_CATALOG = B.TABLE_CATALOG AND
A.TABLE_SCHEMA = B.TABLE_SCHEMA AND
A.TABLE_NAME = B.TABLE_NAME AND
COLUMNPROPERTY (OBJECT_ID (B.TABLE_NAME), B.COLUMN_NAME, 'IsIdentity') = 1 AND
OBJECTPROPERTY (OBJECT_ID (A.TABLE_NAME), 'TableHasIdentity') = 1 AND
A.TABLE_TYPE = 'BASE TABLE'
)
select 'DBCC CHECKIDENT ('''+A.[SCHEMA]+'.'+a.[TABLE]+''', reseed)' command
, ROW_NUMBER() OVER(ORDER BY a.[SCHEMA], a.[TABLE] asc) AS ID
, A.Curr_Value
, a.[TABLE]
into #temp
from cte A
ORDER BY A.[SCHEMA], A.[TABLE]
declare #i int = 1, #count int = (select max(ID) from #temp)
declare #text varchar(max) = ''
select #COUNT= count(1) FROM #temp
WHILE #I <= #COUNT
BEGIN
SET #text = (SELECT command from #temp where ID=#I)
EXEC (#text + ';')
print #text
select Curr_Value OldValue, ident_current([TABLE]) FixValue, [TABLE] from #temp where ID=#I
SET #I = #I + 1
SET #text='';
END
go
maybe someone or something with enough permissions made a mistake by reseeding?
As simple as this:
create table testid (
id int not null identity (1,1) primary key,
data varchar (3)
)
insert into testid (data) values ('abc'),('cde')
DBCC CHECKIDENT ('testid', RESEED, 1)
insert into testid (data) values ('bad')
I want to insert a dynamic number of rows into a table, based on information in that table.
I can do it using the code below, but I'm wondering if there's a way to avoid the loop.
The commented out section was my best attempt at what I was trying to do, but it gave me an error of:
"The reference to column "iCount" is not allowed in an argument to a TOP, OFFSET, or FETCH clause. Only references to columns at an outer scope or standalone expressions and subqueries are allowed here."
DECLARE #TableX TABLE (
TDate DATE
, TType INT
, Fruit NVARCHAR(20)
, Vegetable NVARCHAR(20)
, Meat NVARCHAR(20)
, Bread NVARCHAR(20)
)
INSERT INTO #TableX VALUES
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
, ('2016-11-10',1,'Banana','Beet',NULL,NULL)
, ('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
, ('2016-11-10',1,'Durian','Daikon',NULL,NULL)
, ('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
, ('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
, ('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
, ('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
, ('2016-11-11',2,NULL,NULL,'Venison','Vienna')
SELECT * FROM #TableX
DECLARE #BlankRow TABLE (
ID INT IDENTITY
, TDate DATE
, TType INT
, iCount INT
)
DECLARE #Counter1 INT = 0
, #RowCount INT
; WITH BR1
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 1
GROUP BY TDate, TType
)
, BR2
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 2
GROUP BY TDate, TType
)
INSERT INTO #BlankRow
SELECT ISNULL(BR1.TDate, BR2.TDate) AS TDate,
CASE WHEN ISNULL(BR1.iCount,0) < ISNULL(BR2.iCount,0) THEN 1 ELSE 2 END AS TType,
ABS(ISNULL(BR1.iCount,0) - ISNULL(BR2.iCount,0)) AS iCount
FROM BR1
FULL JOIN BR2
ON BR1.TDate = BR2.TDate
WHILE #Counter1 < (SELECT MAX(ID) FROM #BlankRow)
BEGIN
SET #Counter1 += 1
SET #RowCount = (SELECT iCount FROM #BlankRow WHERE ID = #Counter1)
INSERT INTO #TableX
SELECT TOP (#RowCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
LEFT JOIN #BlankRow br
ON tx.TDate = br.TDate
WHERE br.ID = #Counter1
END
/*INSERT INTO #TableX
SELECT TOP (tx.iCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
JOIN #BlankRow br
ON tx.TDate = br.TDate*/
SELECT *
FROM #TableX
ORDER BY TDate, TType,
ISNULL(Fruit,REPLICATE(CHAR(255),20)),
ISNULL(Vegetable,REPLICATE(CHAR(255),20)),
ISNULL(Meat,REPLICATE(CHAR(255),20)),
ISNULL(Bread,REPLICATE(CHAR(255),20))
The data is silly, I know, but my end goal is to have two different Tablix's in ReportBuilder that end up with the same number of rows so the headers of my groups show up at the same place on the page.
Something like this:
declare #TableX table(TDate date
,TType int
,Fruit nvarchar(20)
,Vegetable nvarchar(20)
,Meat nvarchar(20)
,Bread nvarchar(20)
);
insert into #TableX values
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
,('2016-11-10',1,'Banana','Beet',NULL,NULL)
,('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
,('2016-11-10',1,'Durian','Daikon',NULL,NULL)
,('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
,('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
,('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
,('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
,('2016-11-11',2,NULL,NULL,'Venison','Vienna');
with DataRN as
(
select *
,row_number() over (partition by TDate, TType order by TDate) rn
from #TableX
)
,RowsRN as
(
select tt.TDate
,tt.TType
,td.rn
from (select distinct TDate, TType
from #TableX
) tt
full join (select distinct t1.TDate
,row_number() over (partition by t1.TDate, t1.TType order by t1.TDate) rn
from #TableX t1
) td
on(tt.TDate = td.TDate)
)
select r.TDate
,r.TType
,d.Fruit
,d.Vegetable
,d.Meat
,d.Bread
from DataRN d
full join RowsRN r
on(d.TDate = r.TDate
and d.TType = r.TType
and d.rn = r.rn
)
order by r.TDate
,r.TType
,isnull(d.Fruit,REPLICATE(CHAR(255),20))
,isnull(d.Vegetable,REPLICATE(CHAR(255),20))
,isnull(d.Meat,REPLICATE(CHAR(255),20))
,isnull(d.Bread,REPLICATE(CHAR(255),20))
In response to your comment, here is how you would use another cte to generate the full list of dates that you would need, if you havn't got a Dates reference table already (These are tremendously useful):
declare #MinDate date = (select min(TDate) from #TableX);
declare #MaxDate date = (select max(TDate) from #TableX);
with Dates as
(
select #MinDate as DateValue
union all
select dateadd(d,1,DateValue)
from Dates
where DateValue < #MaxDate
)
select DateValue
from Dates
option (maxrecursion 0);
Is it possible in T-SQL to write a proper query reflecting this pseudo-code:
SELECT {primary_key}, {column_name}
FROM {table}
WHERE {any column_name value} is NULL
i.e. without referencing each column-name explicitly.
Sounds simple enough but I've searched pretty extensively and found nothing.
You have to use dynamic sql to solve that problem. I have demonstrated how it could be done.
With this sql you can pick a table and check the row with id = 1 for columns being null and primary keys. I included a test table at the bottom of the script. Code will not display anything if there is not primary keys and no columns being null.
DECLARE #table_name VARCHAR(20)
DECLARE #chosencolumn VARCHAR(20)
DECLARE #sqlstring VARCHAR(MAX)
DECLARE #sqlstring2 varchar(100)
DECLARE #text VARCHAR(8000)
DECLARE #t TABLE (col1 VARCHAR(30), dummy INT)
SET #table_name = 'test_table' -- replace with your tablename if you want
SET #chosencolumn = 'ID=1' -- replace with criteria for selected row
SELECT #sqlstring = COALESCE(#sqlstring, '') + 'UNION ALL SELECT '',''''NULL '''' '' + '''+t1.column_name+''', 1000 ordinal_position FROM ['+#table_name+'] WHERE [' +t1.column_name+ '] is null and ' +#chosencolumn+ ' '
FROM INFORMATION_SCHEMA.COLUMNS t1
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE t2
ON t1.column_name = t2.column_name
AND t1.table_name = t2.table_name
AND t1.table_schema = t2.table_schema
WHERE t1.table_name = #table_name
AND t2.column_name is null
SET #sqlstring = stuff('UNION ALL SELECT '',''''PRIMARY KEY'''' ''+ column_name + '' '' col1, ordinal_position
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE table_name = ''' + #table_name+ '''' + #sqlstring, 1, 10, '') + 'order by 2'
INSERT #t
EXEC( #sqlstring)
SELECT #text = COALESCE(#text, '') + col1
FROM #t
SET #sqlstring2 ='select '+stuff(#text,1,1,'')
EXEC( #sqlstring2)
Result:
id host_id date col1
PRIMARY KEY PRIMARY KEY PRIMARY KEY NULL
Test table
CREATE TABLE [dbo].[test_table](
[id] int not null,
[host_id] [int] NOT NULL,
[date] [datetime] NOT NULL,
[col1] [varchar](20) NULL,
[col2] [varchar](20) NULL,
CONSTRAINT [PK_test_table] PRIMARY KEY CLUSTERED
(
[id] ASC,
[host_id] ASC,
[date] ASC
))
Test data
INSERT test_table VALUES (1, 1, getdate(), null, 'somevalue')