SQL Server xml and nvarchar columns - sql-server

I wanted to achieve this by the query below , I have assigned the table names to the #table_names variable and want to now check if these tables exist in the database . If they don't then I want to print their names and raise an error .
BUT I think the IF OBJECT_ID(#TABLE_NAMES, 'U') IS NULLis failing and the print command is listing all the tables names regardless of their existence .
DECLARE #TABLE_NAMES nvarchar(MAX) =
(
select distinct B.POP_TABLE_name + ' '
from GEOLEVELS a
left outer join GEOG b
on a.GEOGid=b.GEOGid
where b.POP_TABLE_name is not null and
(a.x_COLUMN is not null and a.y_COLUMN is not null) OR
a.z_column is not null
FOR XML PATH('')
)
IF OBJECT_ID(#TABLE_NAMES, 'U') IS NULL
PRINT 'Table not found : ' + #TABLE_NAMES
RAISERROR('TABLE NOT FOUND %S',16,1,#TABLE_NAMES)

Your code is creating a local variable #TABLE_NAMES, then checking for a USER_TABLE (i.e. 'U') with that name.
Since you are never creating a user table with the name #TABLE_NAMES, your OBJECT_ID(#TABLE_NAMES, 'U') will always be NULL.
I think what you want is something like this:
DECLARE #TABLE_NAMES (ID INT IDENTITY(1,1), Name VARCHAR(255))
INSERT INTO #TABLE_NAMES (Name)
select distinct B.POP_TABLE_name
from GEOLEVELS a
left outer join GEOG b
on a.GEOGid=b.GEOGid
where b.POP_TABLE_name is not null and
(a.x_COLUMN is not null and a.y_COLUMN is not null) OR
a.z_column is not null
DECLARE #Counter INT = (SELECT COUNT(*) FROM #TABLE_NAMES)
DECLARE #CurrentName VARCHAR(255)
DECLARE #TablesNotFound TABLE (TableName VARCHAR(255))
WHILE #Counter > 0
BEGIN
SET #CurrentName = (SELECT Name FROM #TABLE_NAMES WHERE ID = #Counter)
IF OBJECT_ID(#CurrentName) IS NULL
BEGIN
PRINT 'Table not found: ' + #CurrentName
INSERT INTO #TablesNotFound (TableName)
VALUES (#CurrentName)
SET #Counter = #Counter - 1
END
ELSE
BEGIN
SET #Counter = #Counter - 1
END
END
IF (SELECT COUNT(*) FROM #TablesNotFound) > 0
BEGIN
DECLARE #ErrorTables VARCHAR(MAX) = (SELECT STUFF((SELECT ',' + TableName FROM #TablesNotFound ORDER BY TableName FOR XML PATH ('')), 1, 1, ''))
RAISERROR(#ErrorTables,16,1)
END
This will put all of the table names matching your criteria into a table variable. Then, it will iterate over the table (using the ID column and a counter), put the non-existent tables into an error table, then stuff the results into one text variable and push that out with the RAISERROR...it will also accomplish the printing of each table that fails. The issue you are experiencing is that all your table names are being mashed together (though separated by a space) and "SQL" sees this as just one string of text; there is nothing telling it how to distinguish the table names within the string from one another.

Related

How to get only the columns that have at least one non-null value in a table existing in SQL server

I have a table named Product with following data for instance:
p_id
p_name
p_cat
1
shirt
null
2
null
null
3
cap
null
Suppose I don't know numbre of rows and columns in the table as well as I don't know which columns are compltely null (no non-null value in all of its rows). How to write a query to retrieve just the columns that have atleast one non-null value in its rows. My approach is as follows but not getting a corret output:
select
column_name
into #TempColumns
from information_schema.columns
where
table_name = 'Product'
and table_schema = 'DDB'
declare #CurrentColumn nvarchar(max) = '', #IsNull bit, #NonNullCols nvarchar(max) = ''
declare Cur cursor for
select column_name from #TempColumns
open Cur
while 1=1
begin
fetch next from Cur into #CurrentColumn
select #IsNull = case when count(#CurrentColumn) > 0 then 0 else 1 end
from Product
if #IsNull = 1
begin
set #NonNullCols = #NonNullCols + ',' + #CurrentColumn
end
if ##fetch_status <> 0 break
end
close Cur
deallocate Cur
select #NonNullCols as NullColumns
drop table #TempColumns
If there is any other approach or correction in my above (T-SQL) query. Thanks in advance.
First I just create a temporary table to store all the column names avaialbe in the Product table. Then I looped in this temporary table and feteched each row and checked it on the product table whether the column is comptely null or not using the count() function. The condition sets the bit variable 1 if the column is completely null and then that particular column name is stored in anothe variable which is then retrieved as null columns.
Here is a conceptual example for you.
It is using SQL Server XML and XQuery powers without dynamic SQL and cursors/loops.
The algorithm is very simple.
When we are converting each row into XML, columns that hold NULL value are missing from the XML.
SQL
USE tempdb;
GO
DROP TABLE IF EXISTS #tmpTable;
CREATE TABLE #tmpTable (
client_id int,
client_name varchar(500),
client_surname varchar(500),
city varchar(500),
state varchar(500));
INSERT #tmpTable VALUES
(1,'Miriam',NULL,'Las Vegas',NULL),
(2,'Astrid',NULL,'Chicago',NULL),
(3,'David',NULL,'Phoenix',NULL),
(4,'Hiroki',NULL,'Orlando',NULL);
SELECT DISTINCT x.value('local-name(.)', 'SYSNAME') AS NotNULLColumns
FROM #tmpTable AS t
CROSS APPLY (SELECT t.* FOR XML PATH(''), TYPE, ROOT('root')) AS t1(c)
CROSS APPLY c.nodes('/root/*') AS t2(x);
SQL #2
To handle edge cases.
SELECT DISTINCT x.value('local-name(.)', 'SYSNAME') AS NotNULLColumns
FROM #tmpTable AS t
CROSS APPLY (SELECT t.* FOR XML RAW, ELEMENTS, BINARY BASE64, TYPE, ROOT('root')) AS t1(c)
CROSS APPLY c.nodes('/root/row/*') AS t2(x);
Output
NotNULLColumns
city
client_id
client_name

Select on a table with 2 possible structures

I'm trying to write a query that will select data from a table. due to different versions of the database, there are 2 possible structures for the source table, where the newer version has 2 more fields than the old one.
I've tried identifying the older structure and replacing the columns with NULL and also tried writing 2 separate queries with and IF statement directing to the correct one. Neither of these solutions work and in both cases it seems that the SQL engine is failing on validating these 2 columns.
Examples of my attempted solutions:
IF NOT EXISTS (SELECT *
FROM sys.objects
WHERE object_id = Object_id(N'[dbo].[Test2]')
AND type IN ( N'U' ))
BEGIN
CREATE TABLE [dbo].[test2]
(
[id] [INT] IDENTITY(1, 1) NOT NULL,
[statusid] [INT] NULL
)
END
go
DECLARE #Flag INT = 0
IF EXISTS(SELECT 1
FROM sys.columns
WHERE NAME = N'TestId'
AND object_id = Object_id(N'dbo.Test2'))
SET #Flag = 1
--Solution #1
IF #Flag = 1
SELECT id,
statusid,
testid
FROM dbo.test2
ELSE
SELECT id,
statusid
FROM dbo.test2
--Solution #2
SELECT id,
statusid,
CASE
WHEN #Flag = 1 THEN testid
ELSE NULL
END AS TestId
FROM dbo.test2
you can use Dynamic SQL and generate the query accordingly depends on value of #flag
declare #sql nvarchar(max)
select #sql = N'select id, statusid, '
+ case when #flag = 1 then 'testid' else 'NULL' end + ' as testid'
+ ' from dbo.test2'
print #sql
exec sp_executesql #sql
But it will not be that easy to code and maintain Dynamic Query if you have a complex query.

SP to compare the columns and data types of two tables and to alter the target table as per the source table

I'm basically new to SQL and I have a request to create a Stored Procedure to compare the columns of two tables(a source table and a target table).
So first of all in the two tables that are compared(the source table and the target table) if there are no common columns then I need to call a CREATE TABLE procedure to create the source table in the target database or the target schema. I also need help to make the CREATE TABLE procedure.
In the next case scenario if there are matching columns in the two tables them we need to compare the columns.So if in the source table there are like maybe two or three extra columns(even more)compared to the target table then I need to alter the target table so as to add these two or three columns and I also need to alter the data types of the target table (for these added columns) as per the source table. Similarly, if in the target table there are two or three extra columns (or more) as compared to the source table then I need to drop these columns from the target table. So I also need help to call an ALTER TABLE procedure in these cases.
Could anyone help me with the stored procedure?
Not claiming this is perfect, and my naming conventions are terrible, but I bashed this out so wanted to share it with you as I hope it gets you started:
DECLARE #strTargetTable NVARCHAR(20) ,
#strSourceTable NVARCHAR(20) ,
#intCommonColumns INT;
SELECT #strTargetTable = 'Test_Table';
SELECT #strSourceTable = 'Test1';
SELECT #intCommonColumns = COUNT(*)
FROM
(
SELECT TD3.COLUMN_NAME
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD3
WHERE TD3.TABLE_NAME = #strTargetTable
AND EXISTS
(
SELECT TD.COLUMN_NAME
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD
WHERE TD.TABLE_NAME = #strSourceTable
AND TD3.COLUMN_NAME = TD.COLUMN_NAME
)
) AS A
/*No common columns*/
IF(#intCommonColumns = 0)
BEGIN
PRINT 'Create table here';
END
/*Common Columns*/
IF(#intCommonColumns > 0)
BEGIN
/*There are matching columns so figure out what to do with them*/
/*Create table to hold the columns that need creating*/
IF OBJECT_ID('tempdb..#tmpCreateFirstColumns') IS NOT NULL DROP TABLE #tmpCreateFirstColumns
CREATE TABLE #tmpCreateFirstColumns
(
ID INT IDENTITY(1,1) ,
T_COL_NAME NVARCHAR(20) ,
T_DATA_TYPE NVARCHAR(20)
)
INSERT INTO #tmpCreateFirstColumns
(
T_COL_NAME ,
T_DATA_TYPE
)
/*Get columns from the Source table that don't exist in the target*/
SELECT TD3.COLUMN_NAME ,
TD3.DATA_TYPE + CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN '' ELSE '('+CAST(CHARACTER_MAXIMUM_LENGTH AS NVARCHAR(3))+')' END
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD3
WHERE TD3.TABLE_NAME = #strSourceTable
AND NOT EXISTS
(
SELECT TD.COLUMN_NAME
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD
WHERE TD.TABLE_NAME = #strTargetTable
AND TD3.COLUMN_NAME = TD.COLUMN_NAME
)
DECLARE #intMinID INT ,
#intMaxID INT ,
#strSQL NVARCHAR(MAX);
SELECT #intMinID = MIN(TC.ID) ,
#intMaxID = MAX(TC.ID)
FROM #tmpCreateFirstColumns AS TC
WHILE(#intMinID <= #intMaxID)
BEGIN
DECLARE #strCol NVARCHAR(20) ,
#strData1 NVARCHAR(MAX) ;
SELECT #strCol = TC.T_COL_NAME ,
#strData1 = TC.T_DATA_TYPE
FROM #tmpCreateFirstColumns AS TC
WHERE TC.ID = #intMinID;
SELECT #strSQL = 'ALTER TABLE '+#strTargetTable+' ADD '+'['+#strCol+']'+' '+#strData1;
PRINT 'ALTER TABLE '+#strTargetTable+' ADD '+'['+#strCol+']'+' '+#strData1;
EXEC sp_executesql #strSQL
SELECT #intMinID = #intMinID + 1;
END
/*Create table to hold the columns that need creating*/
IF OBJECT_ID('tempdb..#tmpCreateColumns') IS NOT NULL DROP TABLE #tmpCreateColumns
CREATE TABLE #tmpCreateColumns
(
ID INT IDENTITY(1,1) ,
T_COL_NAME NVARCHAR(20) ,
T_DATA_TYPE NVARCHAR(20)
)
INSERT INTO #tmpCreateColumns
(
T_COL_NAME ,
T_DATA_TYPE
)
/*Get columns from the Source table that don't exist in the target*/
SELECT TD3.COLUMN_NAME ,
TD3.DATA_TYPE + CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN '' ELSE '('+CAST(CHARACTER_MAXIMUM_LENGTH AS NVARCHAR(3))+')' END
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD3
WHERE TD3.TABLE_NAME = #strTargetTable
AND NOT EXISTS
(
SELECT TD.COLUMN_NAME
FROM Test_Database3.INFORMATION_SCHEMA.COLUMNS AS TD
WHERE TD.TABLE_NAME = #strSourceTable
AND TD3.COLUMN_NAME = TD.COLUMN_NAME
)
SELECT #intMinID = MIN(TCC.ID) ,
#intMaxID = MAX(TCC.ID)
FROM #tmpCreateColumns AS TCC
WHILE(#intMinID <= #intMaxID)
BEGIN
DECLARE #strCol2 NVARCHAR(20) ,
#strData NVARCHAR(MAX) ;
SELECT #strCol2 = TCC.T_COL_NAME ,
#strData = TCC.T_DATA_TYPE
FROM #tmpCreateColumns AS TCC
WHERE TCC.ID = #intMinID;
SELECT #strSQL = 'ALTER TABLE '+#strSourceTable+' ADD '+'['+#strCol2+']'+' '+#strData;
PRINT 'ALTER TABLE '+#strSourceTable+' ADD '+'['+#strCol2+']'+' '+#strData;
EXEC sp_executesql #strSQL
SELECT #intMinID = #intMinID + 1;
END
END

Using row count from a temporary table in a while loop SQL Server 2008

I'm trying to create a procedure in SQL Server 2008 that inserts data from a temp table into an already existing table. I think I've pretty much figured it out, I'm just having an issue with a loop. I need the row count from the temp table to determine when the loop should finish.
I've tried using ##ROWCOUNT in two different ways; using it by itself in the WHILE statement, and creating a variable to try and hold the value when the first loop has finished (see code below).
Neither of these methods have worked, and I'm now at a loss as to what to do. Is it possible to use ##ROWCOUNT in this situation, or is there another method that would work better?
CREATE PROCEDURE InsertData(#KeywordList varchar(max))
AS
BEGIN
--create temp table to hold words and weights
CREATE TABLE #tempKeywords(ID int NOT NULL, keyword varchar(10) NOT NULL);
DECLARE #K varchar(10), #Num int, #ID int
SET #KeywordList= LTRIM(RTRIM(#KeywordList))+ ','
SET #Num = CHARINDEX(',', #KeywordList, 1)
SET #ID = 0
--Parse varchar and split IDs by comma into temp table
IF REPLACE(#KeywordList, ',', '') <> ''
BEGIN
WHILE #Num > 0
BEGIN
SET #K= LTRIM(RTRIM(LEFT(#KeywordList, #Num - 1)))
SET #ID = #ID + 1
IF #K <> ''
BEGIN
INSERT INTO #tempKeywords VALUES (#ID, #K)
END
SET #KeywordList = RIGHT(#KeywordList, LEN(#KeywordList) - #Num)
SET #Num = CHARINDEX(',', #KeywordList, 1)
--rowcount of temp table
SET #rowcount = ##ROWCOUNT
END
END
--declaring variables for loop
DECLARE #count INT
DECLARE #t_name varchar(30)
DECLARE #key varchar(30)
DECLARE #key_weight DECIMAL(18,2)
--setting count to start from first keyword
SET #count = 2
--setting the topic name as the first row in temp table
SET #t_name = (Select keyword from #tempKeywords where ID = 1)
--loop to insert data from temp table into Keyword table
WHILE(#count < #rowcount)
BEGIN
SET #key = (SELECT keyword FROM #tempKeywords where ID = #count)
SET #key_weight = (SELECT keyword FROM #tempKeywords where ID = #count+2)
INSERT INTO Keyword(Topic_Name,Keyword,K_Weight)
VALUES(#t_name,#key,#key_weight)
SET #count= #count +2
END
--End stored procedure
END
To solve the second part of your problem:
INSERT INTO Keyword(Topic_Name,Keyword,K_Weight)
SELECT tk1.keyword, tk2.keyword, tk3.keyword
FROM
#tempKeywords tk1
cross join
#tempKeywords tk2
inner join
#tempKeywords tk3
on
tk2.ID = tk3.ID - 1
WHERE
tk1.ID = 1 AND
tk2.ID % 2 = 0
(This code should replace everything in your current script from the --declaring variables for loop comment onwards)
You could change:
WHILE(#count < #rowcount)
to
WHILE(#count < (select count(*) from #tempKeywords))
But like marc_s commented, you should be able to do this without a while loop.
I'd look at reworking your query to see if you can do this in a set based way rather than row by row.
I'm not sure I follow exactly what you are trying to achieve, but I'd be tempted to look at the ROW_NUMBER() function to set the ID of your temp table. Used with a recursive CTE such as shown in this answer you could get an id for each of your non empty trimmed words. An example is something like;
DECLARE #KeywordList varchar(max) = 'TEST,WORD, ,,,LIST, SOME , WITH, SPACES'
CREATE TABLE #tempKeywords(ID int NOT NULL, keyword varchar(10) NOT NULL)
;WITH kws (ord, DataItem, Data) AS(
SELECT CAST(1 AS INT), LEFT(#KeywordList, CHARINDEX(',',#KeywordList+',')-1) ,
STUFF(#KeywordList, 1, CHARINDEX(',',#KeywordList+','), '')
union all
select ord + 1, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from kws
where Data > ''
), trimKws(ord1, trimkw) AS (
SELECT ord, RTRIM(LTRIM(DataItem))
FROM kws
)
INSERT INTO #tempKeywords (ID, keyword)
SELECT ROW_NUMBER() OVER (ORDER BY ord1) as OrderedWithoutSpaces, trimkw
FROM trimKws WHERE trimkw <> ''
SELECT * FROM #tempKeywords
I don't fully understand what you are trying to acheive with the second part of your query , but but you could just build on this to get the remainder of it working. It certainly looks as though you could do what you are after without while statements at least.

Sql script syntax and grammar issues

Can someone help please I dont know what I am doing wrong:
IF EXISTS ( SELECT name
FROM sys.tables
WHERE name = N'MemberIdsToDelete' )
DROP TABLE [MemberIdsToDelete];
GO
SELECT mm.memberid ,
mm.aspnetuserid ,
mm.email ,
mm.RowNum AS RowNum
INTO #MemberIdsToDelete
FROM membership.members AS mm
LEFT JOIN aspnet_membership AS asp ON mm.aspnetuserid = asp.userid
LEFT JOIN trade.tradesmen AS tr ON tr.memberid = mm.memberid
WHERE asp.isapproved = 0
AND tr.ImportDPN IS NOT NULL
AND tr.importDPN <> ''
ORDER BY mm.memberid
DECLARE #MaxRownum INT
SET #MaxRownum = ( SELECT MAX(RowNum)
FROM #MemberIdsToDelete
)
DECLARE #Iter INT
SET #Iter = ( SELECT MIN(RowNum)
FROM #MemberIdsToDelete
)
DECLARE #MemberId INT
DECLARE #TrademId INT
DECLARE #UId UNIQUEIDENTIFIER
DECLARE #Successful INT
DECLARE #OutputMessage VARCHAR(200)
DECLARE #Email VARCHAR(100)
DECLARE #Username VARCHAR(100)
SELECT #MemberId = memberId ,
#UId = AspNetUserId
FROM MemberIdsToDelete
SELECT #TrademId = TradesManId
FROM trade.TradesMen
WHERE memberId = #MemberId;
WHILE #Iter <= #MaxRownum
BEGIN
SELECT *
FROM #MemberIdsToDelete
WHERE RowNum = #Iter
--more code here
SET #Iter = #Iter + 1
END
I just want to check if my table MemberIdsToDelete exists, if so drop it,
create MemberIdsToDelete with the results set from the select
loop through MemberIdsToDelete table and perform operations
I am getting error that RowNum does not exist
For a start, to check if a table exists and then drop accordingly, you need to use something like
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = N'MemberIdsToDelete')
DROP TABLE [MemberIdsToDelete];
GO
as for the error, your RowNum column does not exist when you are attempting to reference it. Include it in the SELECT statement
select mm.memberid, mm.aspnetuserid, mm.email, mm.RowNum AS RowNum
into #MemberIdsToDelete
from membership.members as mm
left join aspnet_membership as asp
on mm.aspnetuserid=asp.userid
left join trade.tradesmen as tr
on tr.memberid=mm.memberid
where asp.isapproved = 0 and tr.ImportDPN IS NOT NULL
and tr.importDPN <> ''
order by mm.memberid;
GO
I hope this helps.
Edit. Based on you additional error from your comment. You are now attempting to access a temporary table that does not exist. You must first populate the temporary table #MemberIdsToDelete before attempting to read from it. The invalid column error is down to the same problem. You are attempting to read a column called RowNum from the temporary table which does not exist.
Edit2. Remove the '#' from the #MemberIdsToDelete. You are inserting into a table not a temporary table. Or, Add a # to the select into above (see the code above). This will make it a temporary table as required.
You don't have a RowNum column in that table.
Try:
select mm.memberid, mm.aspnetuserid, mm.email, row_number() over (order by (select 1)) as RowNum
....
This should solve your problem, but I wouldnt actually recommend this idea of looping through the ones to delete.

Resources