Context: I'm having difficulty modifying a stored procedure in SQL Server 2016. The stored procedure performs parsing of json data within a file. For some reason I'm able to execute the stored procedure and it executes successfully but when I try to modify the stored procedure I get the following message:
Question: Does anyone have any troubleshooting tips? Below is the content of the stored procedure. SQL Server 2016 supports the various functions used including the OPENJSON function.
USE mattermark_sandbox
GO
CREATE PROCEDURE get_company_data
AS
IF OBJECT_ID('tempdb..##jsondump') IS NOT NULL DROP TABLE ##jsondump
IF OBJECT_ID('tempdb..##jsonparsed') IS NOT NULL DROP TABLE ##jsonparsed
IF OBJECT_ID('tempdb..##json_loop') IS NOT NULL DROP TABLE ##json_loop
CREATE TABLE ##jsondump (
[my_json] [nvarchar](max) NULL
)
-- Create a table to house the parsed content
CREATE TABLE ##jsonparsed (
[id] [int] NULL,
[url] [varchar](255) NULL,
[company_name] [varchar](255) NULL,
[domain] [varchar](255) NULL
)
-- Clear ##jsondump
TRUNCATE TABLE ##jsondump;
-- Clear ##jsonparsed ( only if you don't want to keep what's already there )
TRUNCATE TABLE ##jsonparsed;
-- Import ( single column ) JSON
--IMPORTANT: Need to be sure the company_data.json file actually exists on the remote server in that directory
BULK INSERT ##jsondump
FROM 'C:\mattermark_etl_project\company_data.json' -- ( <-- my file, point to your own )
WITH (
ROWTERMINATOR = '\n'
);
-- Select JSON into ##jsonparsed
SELECT my_json
INTO ##json_loop
FROM ##jsondump;
--SELECT * FROM ##jsondump;
INSERT INTO ##jsonparsed (
id, [url], company_name, domain
)
SELECT DISTINCT
jsn.id, jsn.[url], jsn.company_name, jsn.domain
FROM ##json_loop
OUTER APPLY (
SELECT * FROM OPENJSON(##json_loop.my_json, '$.companies' )
WITH (
id INT '$.id',
[url] VARCHAR(255) '$.url',
company_name VARCHAR(255) '$.company_name',
domain VARCHAR(255) '$.domain'
)
) AS jsn
DECLARE #bcp_cmd4 VARCHAR(1000);
DECLARE #exe_path4 VARCHAR(200) =
' cd C:\Program Files\Microsoft SQL Server\100\Tools\Binn\ & ';
SET #bcp_cmd4 = #exe_path4 +
' BCP.EXE "SELECT ''Company_ID'', ''MatterMark_URL'', ''Company_Name'', ''Domain'' UNION ALL SELECT DISTINCT cast(id as varchar( 12 )) as id, url, company_name, domain FROM ##jsonparsed" queryout ' +
' "C:\mattermark_etl_project\company_data.txt" -T -c -q -t0x7c -r\n';
PRINT #bcp_cmd4;
EXEC master..xp_cmdshell #bcp_cmd4,no_output;
SELECT DISTINCT * FROM ##jsonparsed
ORDER BY id ASC;
DROP TABLE ##jsondump
DROP TABLE ##jsonparsed
DROP TABLE ##json_loop
/*
-- To allow advanced options to be changed.
EXEC sp_configure 'show advanced options', 1;
GO
-- To update the currently configured value for advanced options.
RECONFIGURE;
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1;
GO
-- To update the currently configured value for this feature.
RECONFIGURE;
GO
*/
exec xp_cmdshell 'C:\mattermark_etl_project\powershell "C:\mattermark_etl_project\open_file.ps1"',no_output
Using SSMS version 18.0 instead of 17.0 seems to be working.
You can download it from https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-2017
You can use a query to view the stored procedure definition without installing a new SSMS.
Before running this query, you can optionally use the SSMS menu item Query -> Results To -> Results to Text to format the result in one text field.
exec sp_helptext [get_company_data]
(Where get_company_data is the stored procedure name.)
Also note that the stored procedure 'Modify' option just opens a regular query tab pre-filled with the definition as an ALTER PROCEDURE, you can instead update it by running an ALTER PROCEDURE in a normal query tab.
There is a workaround for this issue:
Instead of selecting Modify, right klick on the database name and select Tasks> Generate Scripts: Select Specific Objects, Check your object.
If you are using the Oracle server. Then use OPENQUERY. Select Syntax as follows:
select * from OPENQUERY(LINK_SERVER_NAME,'select * from DUAL')
Related
I have around 30-40 CSV files in a folder. For example, suppose folder 'Florida' has customer information from different cities of state Florida. each CSV file has customer information of one city. Now I want to create a table in SQL Server by importing all the CSV files from that folder to create a table for all customers in Florida. I wanted to know if there is any way I could perform this action for all CSV files at once. I am using SQL Server Management Studio (SSMS).
All the CSV files have same column names.
I am doing the following for one CSV file:
CREATE TABLE sales.cust (
Full_name VARCHAR (100) NOT NULL,
phone VARCHAR(50),
city VARCHAR (50) NOT NULL,
state VARCHAR (50) NOT NULL,
);
BULK INSERT sales.cust
FROM 'C:\Users..............\cust1.csv'
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = ',', --CSV field delimiter
ROWTERMINATOR = '\n', --Use to shift the control to next row
ERRORFILE = 'C:\Users\..............\cust1ErrorRows.csv',
TABLOCK
)
Suggestion to use command prompt only because of limited tools.
I thought of another solution you can use that could help you out and make it so you only have to import one file.
Create your table:
CREATE TABLE sales.cust (
Full_name VARCHAR (100) NOT NULL,
phone VARCHAR(50),
city VARCHAR (50) NOT NULL,
state VARCHAR (50) NOT NULL,
);
Using command Prompt do the following:
a. Navigate to your directory using cd "C:\Users..............\"
b. Copy the files into one giant file using:
copy *.csv combined.csv
Import that file using GUI in SSMS
Deal with the headers
delete from sales.cust where full_name = 'Full_name' and phone = 'phone'
You can only do this because all columns are varchar.
Here is one route to get all the files into a table---
-- from Rigel and froadie # https://stackoverflow.com/questions/26096057/how-can-i-loop-through-all-the-files-in-a-folder-using-tsql
-- 1.Allow for SQL to use cmd shell
EXEC sp_configure 'show advanced options', 1 -- To allow advanced options to be changed.
RECONFIGURE -- To update the currently configured value for advanced options.
EXEC sp_configure 'xp_cmdshell', 1 -- To enable the feature.
RECONFIGURE -- To update the currently configured value for this feature.
-- 2.Get all FileNames into a temp table
--for repeatability when testing in SMSS, delete any prior table
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
GO
CREATE TABLE #tmp(csvFileName VARCHAR(100));
INSERT INTO #tmp
EXEC xp_cmdshell 'dir /B "C:\\Users..............\\*.csv"';
-- from Chompy #https://bytes.com/topic/sql-server/answers/777399-bulk-insert-dynamic-errorfile-filename
-- 3.Create sql prototype of the Dynamic sql
---- with CSV field delimiter=',' and CSV shift the control to next row='\n'
DECLARE #sqlPrototype nvarchar(500)
SET #sqlPrototype = N'BULK INSERT sales.cust
FROM ''C:\\Users..............\\xxxx''
WITH ( FIRSTROW = 2,
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
ERRORFILE = ''C:\\Users..............\\xxxx_ErrorRows.txt'',
TABLOCK)'
-- 4.Loop through all of the files
Declare #fileName varchar(100)
While (Select Count(*) From #tmp where csvFileName is not null) > 0
Begin
Select Top 1 #fileName = csvFileName From #tmp
-- 5.Replace real filename into prototype
PRINT(#filename)
DECLARE #sqlstmt nvarchar(500)
Set #sqlstmt = replace(#sqlPrototype, 'xxxx', #filename)
--print(#sqlstmt)
-- 6.Execute the resulting sql
EXEC sp_executesql #sqlstmt;
-- 4A.Remove FileName that was just processed
Delete from #tmp Where csvFileName = #FileName
End
Caution--If ErrorFile exists, then BulkInsert will fail.
I am trying to insert the data of a stored procedure into a temp table like below
CREATE TABLE #CustomTable3HTML
(
ItemId varchar(30),
ItemId1 varchar(30)
)
INSERT INTO #CustomTable3HTML
EXEC SalesDeals.dbo.prGetDealProposalDetail 17100102, 1
but I am getting this error
Msg 8164, Level 16, State 1, Procedure prGetDealProposalDetail, Line 138 [Batch Start Line 1]
An INSERT EXEC statement cannot be nested.
I figured this is because the stored procedure already has an insert into clause defined and I found out that it can be used only once in the calling chain.
So I started looking for other options and found out about OpenRowSet which I am using as below
SELECT *
INTO #CustomTable3HTML
FROM OPENROWSET('SQLOLEDB','Server=Demo\Demo;Trusted_Connection=Yes;Database=SalesDeals',
'SET NOCOUNT ON;SET FMTONLY OFF;EXEC SalesDeals.dbo.prGetDealProposalDetail 17100102,1')
I am getting an error when I run this SQL command
Access to the remote server is denied because no login-mapping exists.
It works fine when I use a higher level account like sysadmin but fails with the other account which is a normal db owner on the database where I am running this SQL.
There is work around of this. It's not beautiful, but it will work.
In our outer query define a table:
CREATE TABLE #CustomTable3HTML
(
ItemId varchar(30),
ItemId1 varchar(30)
)
Change the procedure adding the following code at the end:
IF OBJECT_ID('tempdb..#CustomTable3HTML')
BEGIN
INSERT INTO #CustomTable3HTML
SELECT ....
END
ELSE
BEGIN
SELECT ....
END
After executing the stored procedure you will have the data in table.
Note: the highest linked question does not solve the problem for system stored procedures, but it's close. With help of the commenters, I came to a working answer.
Trying to use statements such as the following for sp_spaceused, throws an error
SELECT * INTO #tblOutput exec sp_spaceused 'Account'
SELECT * FROM #tblOutput
The errors:
Must specify table to select from.
and:
An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name.
When I fully declare a table variable, it works as expected, so it seems to me that the stored procedure does return an actual table.
CREATE TABLE #tblOutput (
name NVARCHAR(128) NOT NULL,
rows CHAR(11) NOT NULL,
reserved VARCHAR(18) NOT NULL,
data VARCHAR(18) NOT NULL,
index_size VARCHAR(18) NOT NULL,
unused VARCHAR(18) NOT NULL)
INSERT INTO #tblOutput exec sp_spaceused 'Response'
SELECT * FROM #tblOutput
Why is it not possible to use a temp table or table variable with the result set of EXECUTE sp_xxx? Or: does a more compact expression exist than having to predefine the full table each time?
(incidentally, and off-topic, Googling for the exact term SELECT * INTO #tmp exec sp_spaceused at the time of writing, returned exactly one result)
TL;DR: use SET FMTONLY OFF with OPENQUERY, details below.
It appears that the link provided by Daniel E. is only part of the solution. For instance, if you try:
-- no need to use sp_addlinkedserver
-- must fully specify sp_, because default db is master
SELECT * FROM OPENQUERY(
[SERVERNAME\SQL2008],
'exec somedb.dbo.sp_spaceused ''Account''')
you will receive the following error:
The OLE DB provider "SQLNCLI10" for linked server "LOCALSERVER\SQL2008" supplied inconsistent metadata for a column. The name was changed at execution time.
I found the solution through this post, and then a blog-post on OPENQUERY, which in turn told me that until SQL2008, you need to use SET FMTONLY OFF. The final solution, which is essentially surprisingly simple (and easier to accomplish since there is no need to specify a loopback linked server), is this:
SELECT * FROM OPENQUERY(
[SERVERNAME\SQL2008],
'SET FMTONLY OFF
EXEC somedb.dbo.sp_spaceused ''Account''')
In addition, if you haven't set DATA-ACCESS, you may get the following error:
Server 'SERVERNAME\SQL2008' is not configured for DATA ACCESS.
This can be remedied by running the following command:
EXEC sp_serveroption 'SERVERNAME\SQL2008', 'DATA ACCESS', TRUE
We cannot SELECT from a stored procedure thats why SELECT * INTO ..Exec sp_ will not work.
To get the result set returned from a store procedure we can INSERT INTO a table.
SELECT INTO statement creates a table on fly and inserts data from the source table/View/Function. The only condition is source table should exist and you should be able to Select from it.
Sql Server doesn't allow you to use SELECT from sp_ therefore you can only use the INSERT INTO statement when executing a stored procedure this means at run time you can add the returned result set into a table and Select from that table at later stage.
INSERT INTO statement requires the destination table name, An existing table. Therefore whether you use a Temp Table, Table variable or Sql server persistent table you will need to create the table first and only they you can use the syntax
INSERT INTO #TempTable
EXECUTE sp_Proc
Using [YOUR DATABASE NAME]
CREATE TABLE [YOURTABLENAME]
(Database_Name Varchar(128),
DataBase_Size VarChar(128),
unallocated_Space Varchar(128),
reserved Varchar(128),
data Varchar(128),
index_size Varchar(128),
unused Varchar(128)
);
INSERT INTO dbo.[YOUR TABLE NAME]
(
Database_Name,
DataBase_Size,
unallocated_Space,
reserved,
data,
index_size,
unused
)
EXEC sp_spaceused #oneresultset = 1
--To get it to return it all as one data set add the nonresultset=1 at the end and viola good to go for writing to a table. :)
I have SQL Server 2008 R2. I have around 150 tables in a database and for each table I have recently created triggers. It is working fine in my local environment.
Now I want to deploy them on my live environment. The question is I want to deploy only the triggers.
I tried the Generate Script wizard but it is creating script with table schema along with triggers, NOT triggers only.
Is there anyway to generate all the triggers drop and create type script?
Forget the wizard. I think you have to get your hands dirty with code. Script below prints all triggers code and stores it into table. Just copy the script's print output or get it from #triggerFullText.
USE YourDatabaseName
GO
SET NOCOUNT ON;
CREATE TABLE #triggerFullText ([TriggerName] VARCHAR(500), [Text] VARCHAR(MAX))
CREATE TABLE #triggerLines ([Text] VARCHAR(MAX))
DECLARE #triggerName VARCHAR(500)
DECLARE #fullText VARCHAR(MAX)
SELECT #triggerName = MIN(name)
FROM sys.triggers
WHILE #triggerName IS NOT NULL
BEGIN
INSERT INTO #triggerLines
EXEC sp_helptext #triggerName
--sp_helptext gives us one row per trigger line
--here we join lines into one variable
SELECT #fullText = ISNULL(#fullText, '') + CHAR(10) + [TEXT]
FROM #triggerLines
--adding "GO" for ease of copy paste execution
SET #fullText = #fullText + CHAR(10) + 'GO' + CHAR(10)
PRINT #fullText
--accumulating result for future manipulations
INSERT INTO #triggerFullText([TriggerName], [Text])
VALUES(#triggerName, #fullText)
--iterating over next trigger
SELECT #triggerName = MIN(name)
FROM sys.triggers
WHERE name > #triggerName
SET #fullText = NULL
TRUNCATE TABLE #triggerLines
END
DROP TABLE #triggerFullText
DROP TABLE #triggerLines
Just in generate scripts wizard in the second step ("Set Scripting Options) press Advanced button=> Table/View Options=> Set Script Triggers to True.
check also this link or this. If you want only triggers just select one table to proceed the next step.
I'm looking to filter the resultset of a stored procedure. What I'd like is something like the following (non-working) syntax:
IF EXISTS ( SELECT 1 FROM (EXEC sp_linkedservers) WHERE srv_name = 'myServer' )
PRINT N'dropping linked servers'
GO
edit - this is just one example, I'd like a general solution if possible
You could put the results of the stored procedure into a temporary table or table variable before this then query that table, applying whatever where clause to that you want.
[Edited]
Like this:
DECLARE #foo TABLE
(
SRV_NAME NVARCHAR(100),
SRV_PROVIDERNAME NVARCHAR(100),
SRV_PRODUCT NVARCHAR(100),
SRV_DATASOURCE NVARCHAR(100),
SRV_PROVIDERSTRING NVARCHAR(100),
SRV_LOCATION NVARCHAR(100),
SRV_CAT NVARCHAR(100)
)
INSERT INTO #foo
EXEC sp_linkedservers
SELECT * FROM #foo WHERE SRV_PRODUCT = 'SQL Server'
You would, of course, change that final where clause to what you would want to filter on.
try this:
-- add 'loopback' linkedserver
if exists (select * from master..sysservers where srvname = 'loopback')
exec sp_dropserver 'loopback'
go
exec sp_addlinkedserver #server = N'loopback',
#srvproduct = N'',
#provider = N'SQLOLEDB',
#datasrc = ##servername
go
select * into #t from openquery(loopback, 'set fmtonly on exec sp_who')
select * from #t
drop table #t
go
Assuming you want it for the exact purpose outlined in your question, and not a general solution, then you could just query sys.servers (SQL 2005+) (or sysservers pre 2005), no need to use the sp_linkedservers stored procedure:
-- SQL 2005+
IF EXISTS ( SELECT 1 FROM sys.servers WHERE name = 'myServer' )
PRINT N'dropping linked servers'
GO
-- SQL 2000
IF EXISTS ( SELECT 1 FROM sysservers WHERE srvname = 'myServer' )
PRINT N'dropping linked servers'
GO
Move the stored procedure to a table-valued user defined function. Keep the existing stored procedure, but just have it call this new function rather than duplicating the logic. Then use the function in your query.