I'm Trying to create a stored procedure that will allow me to pick a start date and end date to get data from and to have a variable table name to write this data to.
I would like to pass in the two dates and the table name as parameters in the stored procedure. Here is that part I'm stuck on. I took out the stored procedure to try and get this working. this way I can see the lines the error is on.
DECLARE #MinDateWeek DATETIME
SELECT #MinDateWeek= DATEADD(WEEK, DATEDIFF(WEEK,0,GETDATE()), -7)
DECLARE #MaxDateWeek DATETIME
SELECT #MaxDateWeek= DATEADD(WEEK, DATEDIFF(WEEK,0,GETDATE()),0)
DECLARE #SQLCommand NVARCHAR(MAX)
SET #SQLCommand = ' --ERROR ON THIS LINE
-- Getting how much space is used in the present
DECLARE #Present Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Present
SELECT VMName
,SUM(CapacityGB-FreeSpaceGB)
FROM VMWareVMGuestDisk
GROUP BY VMName;
-- Getting how much space was used at the reference date
DECLARE #Past Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Past
SELECT VMName
,SUM(CapacityGB-FreeSpaceGB)
FROM VMWareVMGuestDisk
WHERE Cast([Date] AS VARCHAR(20))= '''+CAST(#MinDateWeek AS varchar(20))+'''
GROUP BY VMName;
--Inserting the average growth(GB/DAY) between the 2 dates in a Temporary Table
CREATE TABLE #TempWeek (VMName NVARCHAR(50)
, CapacityGB float(24)
, GrowthLastMonthGB float(24)
, FreeSpace FLOAT(24) )
INSERT INTO #TempWeek
SELECT DISTINCT V.VMName
,SUM(V.CapacityGB)
,SUM(((W1.UseSpace-W2.UseSpace)/(DATEDIFF(DAY,'''+CONVERT(VARCHAR(50),#MaxDateWeek)+''','''+CONVERT(VARCHAR (50),#MaxDateWeek)+'''))))
,SUM(V.FreeSpaceGb)
FROM VMWareVMGuestDisk AS V
LEFT JOIN
#Present AS W1
ON
V.VMName=W1.VMName
LEFT JOIN
#Past AS W2
ON
W1.VMName=W2.VMName
WHERE (CONVERT(VARCHAR(15),Date))='''+CONVERT(VARCHAR(50),#MaxDateWeek)+'''
GROUP BY V.VMName;
-- Checking if there is already data in the table
TRUNCATE TABLE SAN_Growth_Weekly;
--insert data in permanent table
INSERT INTO SAN_Growth_Weekly (VMName,Datacenter,Cluster,Company,DaysLeft,Growth, Capacity,FreeSpace,ReportDate)
SELECT DISTINCT
G.VMName
,V.Datacenter
,V.Cluster
,S.Company
, DaysLeft =
CASE
WHEN G.GrowthLastMonthGB IS NULL
THEN ''NO DATA''
WHEN (G.GrowthLastMonthGB)<=0
THEN ''UNKNOWN''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>0 AND (G.FreeSpace/G.GrowthLastMonthGB) <=30
THEN ''Less then 30 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>30 AND (G.FreeSpace/G.GrowthLastMonthGB)<=60 THEN ''Less then 60 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>60 AND (G.FreeSpace/G.GrowthLastMonthGB)<=90
THEN ''Less then 90 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>90 AND (G.FreeSpace/G.GrowthLastMonthGB)<=180 THEN ''Less then 180 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>180 AND (G.FreeSpace/G.GrowthLastMonthGB)<=365 THEN ''Less then 1 Year''
ELSE ''Over 1 Year''
END
,G.GrowthLastMonthGB
,G.CapacityGB
,G.FreeSpace
,'''+#MaxDateWeek+'''
FROM #tempWeek AS G
RIGHT JOIN VMWareVMGuestDisk AS V
ON V.VMName = G.VMName COLLATE SQL_Latin1_General_CP1_CI_AS
LEFT JOIN Server_Reference AS S
ON G.VMName COLLATE SQL_Latin1_General_CP1_CI_AS=S.[Asset Name]
WHERE '''+CONVERT(VARCHAR(50),#MaxDateWeek)+'''= CONVERT(VARCHAR(50),V.Date);'
EXEC sp_executesql #SQLCommand;
The error I get is
Conversion failed when converting date and/or time from character
string.
Thanks for the help.
Are you forgetting to enclose your Group By in the dynamic sql?:
ALTER PROCEDURE SAN_DISK_GROWTH
#MaxDateWeek DATETIME ,
#MinDateWeek DATETIME
AS
BEGIN
DECLARE #SQLCommand NVARCHAR(MAX)
SELECT #SQLCommand = '
DECLARE #Present Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Present
SELECT VMName
,SUM(CapacityGB - FreeSpaceGB)
FROM VMWareVMGuestDisk
WHERE CONVERT(VARCHAR(15),Date) = '''
+ CONVERT(VARCHAR(50), #MaxDateWeek) + ''' GROUP BY VMName;'
END
Try specifying your date/time values as parameters to the dynamic SQL query. In other words, instead of converting the dates to a varchar, use parameters in the query:
WHERE #MaxDateWeek = V.Date;
And pass the parameters on the call to sp_executesql like so:
EXEC sp_executesql #SQLCommand,
'#MindateWeek datetime, #MaxDateWeek datetime',
#MinDateWeek = #MinDateWeek,
#MaxDateWeek = #MaxDateWeek
Then you won't have to convert your dates to strings.
Note that this does not work for dynamic table names or column names. Those need to be concatenated together as part of the dynamic SQL itself.
For example, if you had a table name variable like this:
declare #TableName sysname
set #TableName = 'MyTable'
And you wanted the dynamic SQL to retrieve data from that table, then you would need to build your FROM statement like this:
set #SQLCommand = N'SELECT ...
FROM ' + #TableName + N' WHERE...
This build the name into the SQL like so:
'SELECT ... FROM MyTable WHERE...'
I have a table in SQL Server with around of 300 columns. I want to create a stored procedure that replaces all the negative numbers with null.
The table has columns of type float, integer, nvarchar and so on.
I try to do it with T-SQL, but can't. Please could help me?.
Thanks.
EDIT:
I need a stored procedure that transforms all numeric columns to null if they are negative. The table is dynamic and may be increasing or decreasing the number of columns.
DECLARE #updateStatement AS VARCHAR(MAX)
SET #updateStatement = null
set #target_table =''
select * from systypes
select #updateStatement = COALESCE(#updateStatement+'UPDATE '+ #TARGET_TABLE+' SET '+c.name+ '= NULL WHERE' + c.name +' >0;' ,'UPDATE '+ #TARGET_TABLE +' SET '+c.name+ '= NULL WHERE' + c.name +' >0;' ) from syscolumns as c
inner join sysobjects as o on c.id= o.id
INNER JOIN systypes as t on c.xtype = t.xtype
where o.name= #TARGET_TABLE
AND T.name in('int','real','money','decimal')
select #updateStatement
--EXEC (#updateStatement)
You will need to add more to the where clause for T.name in () to hit all the data types you want. But this will query all the columns in 1 table #Target_Table then create update statements for each column in that table, if they are negative it will set the ALL ROWS for that column to null.
To make work uncomment out the EXEC(#UpdateStatement) THIS IS A REAL TERRIBLE IDEA, if you are dead set on doing this review your update statements
AND TAKE A BACKUP UP FIRST
EDIT*** THIS WILL NOT TAKE INTO ACCOUNT WEIRD FLOATING POINT MATH
I have a table with companies and one with categories. I'm using SQL Server Free Text search, and searching companies (by name and description) works fine. But now I also want to include the category table.
I want to search for something like: ABC 24 Supermarket.
Now, ABC 24 should make a match with the Name column in the company table, and Supermarket is the name of the category this company is connected to.
Right now I have something like this:
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.[Description], CO.[Name]), #SearchString)
AND CONTAINS(CA.[Description], #SearchString)
But this of course, gives me nothing, because my search string cannot be found in either the company or the category table. Does anyone have an idea on how to do a combined search on my company and category table?
The idea of splitting strings, as suggested in Lobo's answer below, is not really an option. Because i don't know which part will be the one that should match a category and which part should be used for matching company names/descriptions. Users might just as well type in "Supermarket ABC 24".
Imho the right way to do this is to create an indexed view containing the primary key of the main table ('company' in your example) and a second column containing all the stuff you're actually want to search, i.e.
create View View_FreeTextHelper with schemabinding as
select CO.PrimaryKey, -- or whatever your PK is named
CO.description +' '+CA.description +' '+CO.whatever as Searchtext
from dbo.company CO join
dbo.category CA on CA.CategoryId = CO.CategoryId
Note the two-part form of your tables. A few restrictions arise from this, e.g. all involved tables must be in the same table space and as far as I remember, no TEXT columns are allowed in this kind of concatenation (you may cast them though).
Now create a unique index on the PrimaryKey column
create unique clustered index [View_Index]
on View_FreeTextHelper (PrimaryKey ASC)
Finally create the fulltext index on the view using the 'Searchtext' column as the only column to index. Of course, you may add more columns, if you e.g. wish to distinguish in searching for company name and location and, the names of the managers (you would just concatenate them in a second column).
Retrieving your data is now easy:
select tbl.RANK,
co.*
from freetextTable(View_FreeTextHelper,Search,'Your searchtext here') tbl
join company co on tbl.key=co.PrimaryKey
order by tbl.RANK desc
You may also limit the output using select top 50 as the freetexttable clause will eventually return quite a lot of close and not so close results.
And finally, don't get confused if you cannot find thing like 'off the shelf inc.' Beware of the stop lists. These are list of words which are very common, have no semantic use (like the) and are therefore removed from the text to be indexed. In order to include them, you have to switch of the stoplist feature.
A last tipp: full text is very powerful but has a lot of features, tricks and caveats. It takes quite a bit to fully understand the techniques and get the best results you want.
Have fun.
If we assume that the name of columns are unique per row, then you can use below query. The following example returns all rows that contain either the phrase "ABC", "24" or "Supermarket" in each of the columns
DECLARE #SearchString nvarchar(100) = N'ABC 24 Supermarket'
SET #SearchString = REPLACE(LTRIM(RTRIM(#SearchString)), ' ', '|')
SELECT *
FROM Company CO JOIN Category CA ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS(CO.[Name], #SearchString)
AND CONTAINS(CO.[Description], #SearchString)
AND CONTAINS(CA.[Description], #SearchString)
First of all you need to prepare a search value for the CONTAINS predicate used in the WHERE clause. In this case I replaced spaces between the words on the "|" logic operator(the bar symbol (|) may be used instead of the OR keyword to represent the OR operator.)
It occurs to me that while the answer I wrote previously should work fine and be reasonably efficient, processing the search items one-at-a-time and only searching within the existing search result for items after the first, that it would be faster to do it all at once, using dynamic sql.
So here is another potential solution to your problem, which I enter as a separate answer as it is unrelated to the solution I've already posted:
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
DECLARE #AllItems table (
SearchItem varchar(100)
),
#x int,
#cmd varchar(1000),
#wc varchar(8000),
#this varchar(100);
-- break up #SearchString into component search items:
select #x = charindex(' ', #SearchString);
while #x > 0 begin
insert #AllItems (SearchItem) values (substring(#SearchString, 1, #x - 1));
select #SearchString = substring(#searchstring, #x + 1);
select #x = charindex(' ', #Searchstring);
end;
-- add the last item
insert #AllItems (SearchItem) values (#SearchString)
select #cmd = 'select CO.* from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId WHERE';
--now process search items one-at-a-time building up a where clause to plug into #cmd:
while (select count(*) from #AllItems) > 0 begin
select #this = min(SearchItem) from #AllItems;
delete #AllItems where SearchItem = #this;
select #wc = #wc +
'AND (contains ((CO.[Description], [CO.[Name]) ''' + #this + ''') or contains (CA.[Description], ''' + #this + ''') '
end;
--ready to go:
exec (#cmd + substring(#wc, 4)); --substring removes first AND
I dont know whether this will constitute a GREAT answer (I tend to doubt it) but I wanted a problem to work on and I happed to pick yours, so here's my solution:
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
DECLARE #AllItems table (
SearchItem varchar(100)
),
#x int;
-- break up #SearchString into component search items:
select #x = charindex(' ', #SearchString);
while #x > 0 begin
insert #AllItems (SearchItem) values (substring(#SearchString, 1, #x - 1));
select #SearchString = substring(#searchstring, #x + 1);
select #x = charindex(' ', #Searchstring);
end;
-- add the last item
insert #AllItems (SearchItem) values (#SearchString)
DECLARE #this varchar(100), -- = current search item
#found table ( -- table to contain rows matching the current search item
ID int
),
#usable table ( -- table to contain rows matching all search items
ID int -- already tested
);
--now process search items one-at-a-time
while (select count(*) from #AllItems) > 0 begin
select #this = min(SearchItem) from #AllItems;
delete #AllItems where SearchItem = #this;
if (select count(*) from #usable) = 0 begin --first search item
--for the first item, just find the companies matching this item, in either the
--company name or description or category description columns:
insert #found (ID)
select CO.CompanyID
from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID
where contains ((CO.[Description], [CO.[Name]) #this)
or contains (CA.[Description], #this)
end
else begin --other search items
-- for subsequent items, its got to match with the company name or description
-- or category description as above - BUT it's also got to be a company we
-- already identified when processing the previous term
insert #found (ID)
select CO.CompanyID
from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID inner join #usable U on CO.CompanyID = U.ID
where contains ((CO.[Description], [CO.[Name]) #this)
or contains (CA.[Description], #this)
end
--now clear out and re-populate the usable companies table ready for processing the
--next search item
delete #usable;
insert #usable (ID)
select ID
from #found;
--and clear out the current matches table, ready for the next search item
delete #found;
end;
--whatever is in #usable now, is a match with all the component search items, so:
select CO.*
from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId inner join #usable U on CO.CompanyID = U.ID;
The idea here is that we are going to parse the string into different variables then search the other variables for a match.
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"', #A varchar(100), #B varchar(100), #C varchar(100),#index int
set #A = Substring(#searchString, 1, PATINDEX('% %', #searchString) -1)
set #index = PATINDEX('% %', #searchString) + 1
set #B = Substring(#searchString, #index, PATINDEX('% %', #substring(#searchstring, #index, 100)) -1)
Set #index = PATINDEX('% %', #substring(#searchstring, #index, 100)) + 1
set #C = Substring(#searchString, #index, PATINDEX('% %', #substring(#searchstring, #index, 100)) -1)
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CO.[Description] like #A
or CO.[Description] like #B
or CO.[Description] like #c
or CO.[Name] like #A
or CO.[Name] like #B
or CO.[Name] like #C
or CA.[Description] like #A
or CA.[Description] like #B
or CA.[Description] like #C
This code looks ugly to me but it should accomplish the requirements for the user entering up to 3 items to search on. Anyone have suggestions on cleaning it up?
this should work.
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
set #SearchString = replace(#SearchString,' ','" or "')
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.[Description], CO.[Name]), #SearchString)
AND CONTAINS(CA.[Description], #SearchString)
hope helps a bit
Why don't you just reverse your logic? Your example was trying to find the search string within your field values, but what you really want to do is find your field values within your search string, no?
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE (CONTAINS(#SearchString, CO.[Description]) OR CONTAINS(#SearchString, CO.[Name]))
AND CONTAINS(#SearchString, CA.[Description])
( I don't have sql-server installation to try CONTAINS. You can replace the column LIKE '%string%' with CONTAINS(column, 'string') and try.)
See all queries here.
Another update - After reading the other answers and the manual, it seems that you don't need parenthesized values in the contains string unlike I expected. So this should work too - (you may even try ' | ' instead of ' OR '
SELECT CO.name, CA.description FROM company CO
INNER JOIN category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), REPLACE('ABC 25 SuperMarket', ' ', ' OR '))
AND
CONTAINS(CA.description, REPLACE('ABC 25 SuperMarket', ' ', ' OR '))
If it complains about syntax error near replace, you can create a search string DECLARE #SearchString varchar(MAX) = REPLACE('ABC 25 SuperMarket',' ', ' OR ') and then use it in place of replace(......) as second argument.
Update as per your modified question -
Firstly, you should be moving the logic to application level if possible. I think, it is too much to handle it here. I have come up with this query, but note that this will split each word and search for it in both name and description so you will end up getting a few more results than what you might think. For e.g. this will return all Supermarket which have either ABC or 24 in their name compared to returning only one Supermarket with name ABC 24 in my previous query. This should actually help you out because, as per you, user might just type "ABC Supermarket 24" or "24 ABC Supermarket" or ...
DECLARE #SearchString varchar(MAX) = 'ABC 24 SuperMarket'
DECLARE #separator varchar(MAX) = ' '
DECLARE #Like1 varchar(MAX) = 'CO.name LIKE'
DECLARE #Like2 varchar(MAX) = 'CA.description LIKE'
DECLARE #WHERE1 varchar(MAX) = '( ' + #Like1 + ' ''%' +
REPLACE(#SearchString,#separator,'%'' OR ' + #Like1 + ' ''%')+'%'')'
DECLARE #WHERE2 varchar(MAX) = '( ' + #Like2 + ' ''%' +
REPLACE(#SearchString,#separator,'%'' OR ' + #Like2 + ' ''%')+'%'')'
DECLARE #QueryString varchar(MAX) =
CONCAT('SELECT CO.name, CA.description FROM company CO
INNER JOIN category CA
ON CA.CategoryId = CO.CategoryId
WHERE ', #WHERE1, ' AND ', #WHERE2)
exec(#QueryString);
If you output #WHERE1 you should see
( CO.name LIKE '%ABC%' OR CO.name LIKE '%25%' OR CO.name LIKE '%SuperMarket%')
As I said before you might want to try using CONTAINS with parenthesized values like
DECLARE #SearchString varchar(MAX) = 'ABC 25 SuperMarket'
DECLARE #separator varchar(MAX) = ' '
DECLARE #WHEREString varchar(MAX) = '''"' +
REPLACE(#SearchString, #separator, '" OR "')+'"'''
SELECT CO.name, CA.description FROM company CO
INNER JOIN category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), #WHEREString)
AND
CONTAINS(CA.description, #WHEREString)
If you output #WHEREString you should see
'"ABC" OR "25" OR "SuperMarket"'
Previous answer:
This will assumes that the word after the last space is the description and rest is the `name.
You can split the search string and use them as shown below. This query is using like as I don't have a sql-server installation.
DECLARE #SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE #searchLength int = len(#SearchString)
DECLARE #searchReverse VARCHAR(100) = reverse(#SearchString)
SELECT CO.name, CA.description FROM company CO
INNER JOIN category CA
ON CA.CategoryId = CO.CategoryId
WHERE CO.name LIKE concat( '%', SUBSTRING(#SearchString,0,#searchLength-charindex(' ',#searchReverse)+1), '%')
AND
CA.description LIKE concat( '%', SUBSTRING(#SearchString,#searchLength-charindex(' ',#searchReverse)+2,#searchLength), '%')
This should work. Please note that the where clause is using AND instead of OR.
DECLARE #SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE #searchLength int = len(#SearchString)
DECLARE #searchReverse VARCHAR(100) = reverse(#SearchString)
DECLARE #company VARCHAR(100) = SUBSTRING(#SearchString,0,#searchLength-charindex(' ',#searchReverse)+1)
DECLARE #category VARCHAR(100) = SUBSTRING(#SearchString,#searchLength-charindex(' ',#searchReverse)+2,#searchLength)
SELECT CO.name, CA.description FROM company CO
INNER JOIN category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name, CO.description), #company)
AND
CONTAINS(CA.description , #category)
you can use FREETEXT instead of CONTAIN.
DECLARE #SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE FREETEXT((CO.[Description], CO.[Name]), #SearchString)
OR FREETEXT(CA.[Description], #SearchString)
I'm trying to do a select from a table that will need to be in a variable. I'm working with tables that are dynamically created from an application. The table will be named CMDB_CI_XXX, where XXX will be an integer value based on a value in another table. The ultimate goal is to get the CI Name from the table.
I've tried passing the pieces that make up the table name to a function and string them together and then return the name value, but I'm not allowed to use an EXEC statement in a function.
This is what I want to execute to get the name value back:
Select [Name] from 'CMDB_CI_' + C.CI_TYPE_ID + Where CI_ID = c.CI_ID
This is the code in the SP that I'd like to use the function in to get the name value:
SELECT
CI_ID,
C.CI_TYPE_ID,
CI_CUSTOM_ID,
STATUS,
CI_TYPE_NAME,
--(Select [Name] from CMDB_CI_ + C.CI_TYPE_ID + Where CI_ID = c.CI_ID)
FROM [footprints].[dbo].[CMDB50_CI_COMMON] c
join [footprints].[dbo].[CMDB50_CI_TYPE] t
on c.CI_TYPE_ID = t.CI_TYPE_ID
where status <> 'retired'
order by CI_TYPE_NAME
I'm not sure what to do with this. Please help?
Thanks,
Jennifer
-- This part would be a SP parameter I expect
DECLARE #tableName varchar(100)
SET #tableName = 'CMDB_CI_508'
-- Main SP code
DECLARE #sqlStm VARCHAR(MAX)
SET #sqlStm = 'SELECT *
FROM '+ #tableName
EXEC (#sqlStm)
Fiddle http://sqlfiddle.com/#!3/436a7/7
First off, yes, I know it's a bad design. I didn't design it. It came with the problem tracking software that my company bought for our call center. So I gave up altogether on the approach I was going for and used a cursor to pull all the the names from the various tables into one temp table and then used said temp table to join to the original query.
ALTER Proc [dbo].[CI_CurrentItems]
As
Declare #CIType nvarchar(6)
Declare #Qry nvarchar(100)
/*
Create Table Temp_CI
( T_CI_ID int,
T_CI_Type_ID int,
T_Name nvarchar(400)
)
*/
Truncate Table Temp_CI
Declare CI_Cursor Cursor For
select distinct CI_TYPE_ID FROM [footprints].[dbo].[CMDB50_CI_COMMON]
where STATUS <> 'Retired'
Open CI_Cursor
Fetch Next from CI_Cursor into #CIType
While ##FETCH_STATUS = 0
BEGIN
Set #Qry = 'Select CI_ID, CI_Type_ID, Name from Footprints.dbo.CMDB50_CI_' + #CIType
Insert into Temp_CI Exec (#Qry)
Fetch Next from CI_Cursor into #CIType
END
Close CI_Cursor
Deallocate CI_Cursor
SELECT CI_ID,
C.CI_TYPE_ID,
CI_CUSTOM_ID,
STATUS,
CI_TYPE_NAME,
T_Name
FROM [footprints].[dbo].[CMDB50_CI_COMMON] c
JOIN [footprints].[dbo].[CMDB50_CI_TYPE] t
ON c.CI_TYPE_ID = t.CI_TYPE_ID
JOIN Temp_CI tc
ON c.CI_ID = tc.T_CI_ID
AND t.CI_TYPE_ID = tc.T_CI_TYPE_ID
WHERE STATUS <> 'retired'
ORDER BY CI_TYPE_NAME
I need to export data from a non-normalized database where there are multiple columns to a new normalized database.
One example is the Products table, which has 30 boolean columns (ValidSize1, ValidSize2 ecc...) and every record has a foreign key which points to a Sizes table where there are 30 columns with the size codes (XS, S, M etc...). In order to take the valid sizes for a product I have to scan both tables and take the value SizeCodeX from the Sizes table only if ValidSizeX on the product is true. Something like this:
Products Table
--------------
ProductCode <PK>
Description
SizesTableCode <FK>
ValidSize1
ValidSize2
[...]
ValidSize30
Sizes Table
-----------
SizesTableCode <PK>
SizeCode1
SizeCode2
[...]
SizeCode30
For now I am using a "template" query which I repeat for 30 times:
SELECT
Products.Code,
Sizes.SizesTableCode, -- I need this code because different codes can have same size codes
Sizes.Size_1
FROM Products
INNER JOIN Sizes
ON Sizes.SizesTableCode = Products.SizesTableCode
WHERE Sizes.Size_1 IS NOT NULL
AND Products.ValidSize_1 = 1
I am just putting this query inside a loop and I replace the "_1" with the loop index:
SET #counter = 1;
SET #max = 30;
SET #sql = '';
WHILE (#counter <= #max)
BEGIN
SET #sql = #sql + ('[...]'); -- Here goes my query with dynamic indexes
IF #counter < #max
SET #sql = #sql + ' UNION ';
SET #counter = #counter + 1;
END
INSERT INTO DestDb.ProductsSizes EXEC(#sql); -- Insert statement
GO
Is there a better, cleaner or faster method to do this? I am using SQL Server and I can only use SQL/TSQL.
You can prepare a dynamic query using the SYS.Syscolumns table to get all value in row
DECLARE #SqlStmt Varchar(MAX)
SET #SqlStmt=''
SELECT #SqlStmt = #SqlStmt + 'SELECT '''+ name +''' column , UNION ALL '
FROM SYS.Syscolumns WITH (READUNCOMMITTED)
WHERE Object_Id('dbo.Products')=Id AND ([Name] like 'SizeCode%' OR [Name] like 'ProductCode%')
IF REVERSE(#SqlStmt) LIKE REVERSE('UNION ALL ') + '%'
SET #SqlStmt = LEFT(#SqlStmt, LEN(#SqlStmt) - LEN('UNION ALL '))
print ( #SqlStmt )
Well, it seems that a "clean" (and much faster!) solution is the UNPIVOT function.
I found a very good example here:
http://pratchev.blogspot.it/2009/02/unpivoting-multiple-columns.html