I'm a beginner at SQL Server.
I write this query and pass to this table name and count number with #tblName and #count
DECLARE #Base nvarchar(200)
if (object_ID('tempdb..##temp')) is not NULL
DROP TABLE ##temp
SET #Base = 'WITH Base AS (SELECT [picName],[Address],ID, ROW_NUMBER() OVER (ORDER BY Id DESC) RN FROM'
+ Quotename(#tblName) + ' GROUP BY [picName],[Address],ID)
SELECT * INTO ##temp FROM Base'
EXEC (#Base)
SELECT *
FROM ##temp
declare #command nvarchar(max)
set #command='SELECT TOP 15 [picName],[Address],(SELECT TOP 1 COUNT(Id) FROM ' + QUOTENAME(#tblName) + ') as AllSampleCount FROM ' + QUOTENAME(#tblName) +
'WHERE [Id] IN (SELECT TOP 15 Id From ##temp WHERE RN >'+ ((QUOTENAME(#Count)-1)*15)+ 'ORDER BY Id DESC) ORDER BY Id DESC'
exec (#command)
drop table ##temp
but I get this error
Conversion failed when converting the nvarchar value 'SELECT TOP 15 [picName],[Address],(SELECT TOP 1 COUNT(Id) FROM [Brochure]) as AllSampleCount FROM [Brochure]WHERE [Id] IN (SELECT TOP 15 Id From ##temp WHERE RN >' to data type int.
COUNT IS NUMBER SO CONVERTION TO STR IS NEEDED
QUOTENAME(CONVERT(NVARCHAR(10),#Count-1)*15 ) + 'ORDER BY Id DESC) ORDER BY Id DESC'
replace this:
You need to Convert #Count to nvarchar(20)
set #command='SELECT TOP 15 [picName],[Address],(SELECT TOP 1 COUNT(Id) FROM ' + QUOTENAME(#tblName) + ') as AllSampleCount FROM ' + QUOTENAME(#tblName) +
'WHERE [Id] IN (SELECT TOP 15 Id From ##temp WHERE RN >'+
QUOTENAME(CAST((#Count-1)*15 as NVARCHAR(20))) + ' ORDER BY Id DESC) ORDER BY Id DESC'
In this fragment:
((QUOTENAME(#Count)-1)*15)
the argument of QUOTENAME is #Count but my guess is the actual argument was supposed be (#Count-1)*15. The problem is how the brackets are placed. The -1 (and later *15) is logically applied to the result of QUOTENAME rather than to #Count. That causes the entire concatenation expression to be treated as numeric and SQL Server, therefore, tries to convert all the arguments in it to numbers. It fails to convert the very first one, 'SELECT TOP 15 ...', and that is what the error is about.
If you rewrite the above QUOTENAME fragment like this:
QUOTENAME((#Count-1)*15)
the issue will be gone.
Note that QUOTENAME expects a string and your argument is numeric. In this situation you can either convert the numeric expression's result to a string explicitly (using CAST or CONVERT), as others have suggested, or leave it without explicit conversion, as above – it will be converted automatically.
Related
I have seen multiple questions on how to retrieve every column from every table along with its data type, among many other pieces of information which can be summarised in the shortest way with this query:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
However, is it possible to get all the data from the columns and the rows they belong to get the first row in the table alongside this? I have not found a way to do so thus far. Is it possible to do such, maybe also having a WHERE condition such as checking if the table contains a list of specific columns before returning it e.g.:
SELECT <AllTablesAndColumns+FirstRow>
FROM <WhereTheyCanBeSelectedFrom>
WHERE <TheTableHasTheseSpecificColumns>
Which would return the table name, column name and the data contained within those columns for each row.
If you are looking for more of an EAV structure
Let's say that we're looking for all tables with a column name of ZIPCODE
Example
Declare #S varchar(max) = ''
SELECT #S = #S +'+(Select top 1 SourceTable='''+A.Table_Name+''',* from '+quotename(A.Table_Name)+' for XML RAW)'
FROM INFORMATION_SCHEMA.COLUMNS A
Where COLUMN_NAME in ('ZipCode')
Declare #SQL varchar(max) = '
Declare #XML xml = '+stuff(#S,1,1,'')+'
Select SourceTable = r.value(''#SourceTable'',''varchar(100)'')
,Item = attr.value(''local-name(.)'',''varchar(100)'')
,Value = attr.value(''.'',''varchar(max)'')
From #XML.nodes(''/row'') as A(r)
Cross Apply A.r.nodes(''./#*'') AS B(attr)
Where attr.value(''local-name(.)'',''varchar(100)'') not in (''SourceTable'')
'
Exec(#SQL)
Returns
You could build dynamic query:
DECLARE #sql NVARCHAR(MAX) =
N'SELECT *
FROM (VALUES (1)) AS s(n)
<joins>';
DECLARE #joins NVARCHAR(MAX)= '';
SELECT #joins += FORMATMESSAGE('LEFT JOIN (SELECT TOP 1 * FROM %s ) AS sub%s
ON 1=1' + CHAR(10), table_schema + '.' + table_name,
CAST(ROW_NUMBER() OVER(ORDER BY 1/0) AS VARCHAR(10)))
FROM (SELECT DISTINCT table_schema, table_name
FROM INFORMATION_SCHEMA.COLUMNS
-- WHERE ... -- custom logic based on column type/name/...
) s;
SET #sql = REPLACE(#sql, '<joins>', #joins);
PRINT #sql;
EXEC(#sql);
DBFiddle Demo
The dynamic query has structure:
SELECT *
FROM (VALUES (1)) AS s(n) -- always 1 row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab1 ) AS sub1 ON 1=1 -- get single row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab2 ) AS sub2 ON 1=1
LEFT JOIN (SELECT TOP 1 * FROM dbo.tabC ) AS sub3 ON 1=1
Please treat it as starting point. You could easily extend it with WHERE condition for each subquery and return specific columns instead of *.
EDIT:
Version with UNION ALL:
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = COALESCE(#sql + ' UNION ALL', '') +
FORMATMESSAGE(' SELECT TOP 1 tab_name=''%s'',col_name=''%s'',col_val=%s FROM %s'+CHAR(10)
,table_name, column_name, column_name, table_schema + '.' + table_name)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'colV%';
PRINT #sql;
EXEC(#sql);
DBFiddle Demo2
I need to get a quick overlook of the data in a MS SQL datase and found the following code which gives me all but the last column I need. This third column should show data from the first row.
SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
So my question is how I should formulate the SQL query to get a third column with data from the first row)?
//Update
This code gives me all I want except what table the data comes from. (In a similar question answered by Yaroslav at Select the first 3 rows of each table in a database)
DECLARE #sql VARCHAR(MAX)='';
SELECT #sql=#sql+'SELECT TOP 3 * FROM '+'['+SCHEMA_NAME(schema_id)+'].['+name+']'+';'
FROM sys.tables
EXEC(#sql)
One approach, if I get this correctly, was an undocumented stored procedure sp_MsForeachTable. The questionmark is a placeholder for the table's name there:
Hint: edit "YourDataBase"...
EXEC sp_msforeachtable 'USE YourDataBase;SELECT TOP 1 ''?'' AS TableName, * FROM ?';
With this query you can explore all first rows easily
CREATE TABLE #test(TableName NVARCHAR(MAX),Content XML)
EXEC sp_msforeachtable
'USE YourDataBase;INSERT INTO #test SELECT ''?'' AS TableName, (SELECT TOP 1 * FROM ? FOR XML PATH(''row''))';
SELECT * FROM #test;
UPDATE
Your own code would return the table's name also. Try this
DECLARE #sql VARCHAR(MAX)='';
SELECT #sql=#sql+'SELECT TOP 3 ''' + t.[name] + ''' AS TableName, * FROM '+'['+SCHEMA_NAME(schema_id)+'].['+name+']'+';'
FROM sys.tables t
EXEC(#sql)
I know this isnt much but it build you a select statement, you can then loop through each statement exec each statement or write a union, But I guess it could be a good starting point
Edit: You could also write a loop and exec each statement in a loop insert the value into a final table, Then just select from that table and you should be good.
SELECT
t.String
,t.q
,t.TABLE_NAME
,t.q2
,t.tbname
,t.com2
,t.q4
,t.COLUMN_NAME
,t.q5
,t.Columnname
,t.com3
,t.ColName
,t.[From]
,t.FromSelect
FROM (SELECT
'Select top 1 ' AS String
,'''' q
,TABLE_NAME
,'''' q2
,'as TableName'
as tbname
,',' com2
,'''' q4
,COLUMN_NAME
,'''' q5
,'as COLUMN_NAME'
as Columnname
,',' com3
,COLUMN_NAME as ColName
, 'Value From ' as [From]
,TABLE_NAME as FromSelect
,ROW_NUMBER() OVER (PARTITION BY TABLE_NAME ORDER BY TABLE_NAME DESC, COLUMN_NAME) rn
FROM INFORMATION_SCHEMA.COLUMNS c
) t
WHERE rn = 1;
The result will be something like this for each table.
Select top 1 ' zipcodes ' as TableName , ' CITY ' as COLUMN_NAME , CITY Value From zipcodes
Select top 1 ' _BHCAMERAPRICE ' as TableName , ' _BHID ' as COLUMN_NAME , _BHID Value From _BHCAMERAPRICE
I have a table (DataImportTable) that contains column names of the MainDataTable as its records - please see image:
Target Column Table
I would like to use the above-mentioned column names in a query to look only them up from my MainDataTable.
The query that obviously doesn't work, however can explain what I am trying to achieve, can be seen below:
select (select TargetColumn from DataImportTable) from MainDataTable
I hope this makes sense and that someone can be of assistance.
Thank you in advance!
You could do something like this:
declare #query varchar(max);
declare #columns varchar(max);
set #columns = (select TargetColumn + ', ' from DataImportTable for xml path(''));
set #query = 'select ' + left(#columns, len(#columns) -1) + ' from MainDataTable';
exec(#query);
First #columns is constructed as the values of the TargetColumn, comma-separated (with a trailing comma). Then, the columns are used to construct a query (the left-thing removes the trailing comma), which is in turn executed.
Use Dynamic SQL to get just the columns that are present in other table
Select row_number() over( order by TargetColumn ) as RowNumber,TargetColumn into
#temp from DataImportTable where TargetColumn is not null
Declare #Columnslist varchar(max),#i int
set #i = 1
while #i<= (select Count(*) from #temp)
begin
(SELECT #Columnslist = isnull(#Columnslist,'')+'['+(select TargetColumn from #temp
where RowNumber = #i)+'],')
set #i = #i + 1;
end
SELECT #Columnslist = LEFT(#Columnslist, LEN(#Columnslist) - 1)
Declare #select_cmd varchar(max)
set #select_cmd='select '+#Columnslist +' From MainDataTable'
EXEC(#select_cmd);
this answer makes it even simpler
exec('select '+(select Stuff((select ','+TargetColumn from DataImportTable
for xml path('')),1,1,''))+' From MainDataTable')
try select (select TargetColumn from DataImportTable) as TargetColumn from MainDataTable
What's wrong with this MySQL query? SELECT * AS `x`, how to use x again later?
Does anyone know how to check a a variable against all database table with columns storing the same type of information? I have a poorly designed database that stores ssn in over 60 tables within one database. some of the variations of columns in the various tables include:
app_ssn
ca_ssn
cand_ssn
crl_ssn
cu_ssn
emtaddr_ssn
re_ssn
sfcart_ssn
sfordr_ssn
socsecno
ssn
Ssn
SSN
I want to create a stored procedure that will accept a value and check it against every table that has 'ssn' in the name.Does anyone have idea as to how to do this?
-- I assume that table/column names don't need to be surrounded by square braces. You may want to save matches in a table - I just select them. I also assume ssn is a char.
alter proc proc1
#search1 varchar(500)
as
begin
set nocount on
declare #strsql varchar(500)
declare #curtable sysname
declare #prevtable sysname
declare #column sysname
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
order by table_schema+'.'+table_name +column_name
-- make sure that at least one column has ssn in the column name
if #curtable is not null
begin
while (1=1)
begin
set #strsql = 'select * from ' +#curtable +' where '+''''+#search1+''''+ ' = '+#column
print #strsql
-- any matches for passed in ssn will match here...
exec (#strsql)
set #prevtable = #curtable+#column
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
and table_schema+'.'+table_name +column_name> #prevtable
order by table_schema+'.'+table_name +column_name
-- when we run out of columns that contain ssn we are done...
if ##ROWCOUNT = 0
break
end
end
end
What you will need to do is some research. But here is where you can start;
SELECT tbl.NAME AS TableName
,cl.NAME AS ColumnName
,IDENTITY(INT, 1, 1) AS ID
INTO #ColumnsToLoop
FROM sys.tables tbl
JOIN sys.columns cl ON cl.object_id = tbl.object_id
This will give you the table / column relation then you can simply build a dynamic SQL string based on each row in the query above (basically loop it) and use EXEC or sp_execsql. So basically;
DECLARE #Loop int = (select min(ID) From #ColumnsToLoop),#MX int = (Select MAX(ID) From #ColumnsToLoop)
WHILE(#Loop<=#MX)
BEGIN
DECLARE #SQL nvarchar(MAX) = 'SQL String'
//Construct the dynamic SQL String
EXEC(#SQL);
SET #Loop += 1
END
Perhaps I went a little too crazy with this one, but let me know. I thought it would best the primary key of the search results with the table name so you could join it to your tables. I also managed to do it without a single cursor or loop.
DECLARE #SSN VARCHAR(25) = '%99%',
#SQL VARCHAR(MAX);
WITH CTE_PrimaryKeys
AS
(
SELECT TABLE_CATALOG,
TABLE_SCHEMA,
TABLE_NAME,
column_name
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE D
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
),
CTE_Columns
AS
(
SELECT A.*,
CONCAT(A.TABLE_CATALOG,'.',A.TABLE_SCHEMA,'.',A.TABLE_NAME) AS FullTableName,
CASE WHEN B.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IsPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS A
LEFT JOIN CTE_PrimaryKeys B
ON A.TABLE_CATALOG = B.TABLE_CATALOG
AND A.TABLE_SCHEMA = B.TABLE_SCHEMA
AND A.TABLE_NAME = B.TABLE_NAME
AND A.COLUMN_NAME = B.COLUMN_NAME
),
CTE_Select
AS
(
SELECT
'SELECT ' +
--This returns the pk_col casted as Varchar and the table name in another columns
STUFF((SELECT ',CAST(' + COLUMN_NAME + ' AS VARCHAR(MAX)) AS pk_col,''' + B.TABLE_NAME + ''' AS Table_Name'
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
AND B.IsPrimaryKey = 1
FOR XML PATH ('')),1,1,'')
+ ' FROM ' + fullTableName +
--This is where I list the columns where LIKE desired SSN
' WHERE ' +
STUFF((SELECT COLUMN_NAME + ' LIKE ''' + #SSN + ''' OR '
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
--This is where I filter so I only get desired columns
AND (
--Uncomment the Collate if your database is case sensitive
COLUMN_NAME /*COLLATE SQL_Latin1_General_CP1_CI_AS*/ LIKE '%ssn%'
--list your column Names that don't have ssn in them
--OR COLUMN_NAME IN ('col1','col2')
)
FOR XML PATH ('')),1,0,'') AS Selects
FROM CTE_Columns A
GROUP BY A.FullTableName,A.TABLE_NAME
)
--Unioning them all together and getting rid of last trailing "OR "
SELECT #SQL = COALESCE(#sql,'') + SUBSTRING(selects,1,LEN(selects) - 3) + ' UNION ALL ' + CHAR(13) --new line for easier debugging
FROM CTE_Select
WHERE selects IS NOT NULL
--Look at your code
SELECT SUBSTRING(#sql,1,LEN(#sql) - 11)
I have a one to many relationship between Course and Facilitator. The foreign key is in the Course table. How do I select a facilitator as distinct and have its multiple course IDs as columns next to the facilitator.
SELECT dbo.Facilitator.Fac_ID, dbo.Facilitator.Fac_Name, dbo.Course.Course_ID
FROM dbo.Course
RIGHT JOIN dbo.Facilitator ON dbo.Course.FK_Facilitator = dbo.Facilitator.Fac_ID
order by dbo.Facilitator.Fac_Name asc
returns:
instead, I want:
For pivoting, the column names should be defined. In your case you you do not have such a column name, as per your question. So we create sample column names like 1,2 etc.
First of all, get the column names for pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + COLUMNNAME + ']', '[' + COLUMNNAME + ']')
FROM
(
-- Generates random column names numerically from 1,2 etc
SELECT DISTINCT CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 0))AS VARCHAR(4))COLUMNNAME
FROM #TEMP
) PV
ORDER BY CAST(COLUMNNAME AS INT)
Now pivot the result. I have written the logic inside.
DECLARE #query NVARCHAR(MAX)
SET #query = '-- This outer query forms your pivoted result
SELECT * FROM
(
-- Source data for pivoting
SELECT ID,NAME,VALUE,
CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 0))AS VARCHAR(4)) COLUMNNAME
FROM #TEMP
) x
PIVOT
(
--Defines the values in each dynamic columns
MIN(VALUE)
-- Get the names from the #cols variable to show as column
FOR COLUMNNAME IN (' + #cols + ')
) p
ORDER BY NAME;'
EXEC SP_EXECUTESQL #query
Click here to view result