RODBC command 'sqlQuery' has problems with table variables in t-SQL - r

I am using the RODBC package which I am applying on a Microsoft SQL Server 2012.
Now I have discovered a phenomenon that puzzles me.
If I run the following query with the RODBC command sqlQuery, then, in R, I will get back an empty data frame with the columns Country, CID, PriceID and WindID.
DECLARE #tbl_IDs TABLE
(
Country nvarchar(30),
CID nvarchar(5),
PriceID int,
WindID int
)
SELECT * FROM #tbl_Ids
So far, everything is fine.
However, if I try to write a record to the table variable and execute
DECLARE #tbl_IDs TABLE
(
Country nvarchar(30),
CID nvarchar(5),
PriceID int,
WindID int
)
INSERT INTO #tbl_IDs
VALUES
('Germany', 'DE', 112000001, 256000002);
SELECT * FROM #tbl_Ids
Then, in R, the result will be an empty character instead of a dataframe with one record. Still the same query works perfectly with SQL Server Management Studio.
Also, we have traced the behaviour of the DB Server while the R-Query is executed and it seems the server handles it perfectly. It seems that the RODBC interface has a problem to return the result to the R console.
Does anybody have an idea how this issue can be resolved?

Try toggling NOCOUNT as below:
old_qry <- "
DECLARE #tbl_IDs TABLE
(
Country nvarchar(30),
CID nvarchar(5),
PriceID int,
WindID int
)
INSERT INTO #tbl_IDs
VALUES
('Germany', 'DE', 112000001, 256000002);
SELECT * FROM #tbl_Ids
"
##
new_qry <- "
SET NOCOUNT ON;
DECLARE #tbl_IDs TABLE
(
Country nvarchar(30),
CID nvarchar(5),
PriceID int,
WindID int
);
INSERT INTO #tbl_IDs
VALUES
('Germany', 'DE', 112000001, 256000002);
SET NOCOUNT OFF;
SELECT * FROM #tbl_Ids
"
R> sqlQuery(tcon, gsub("\\n", " ", old_qry))
#character(0)
R> sqlQuery(tcon, gsub("\\n", " ", new_qry))
# Country CID PriceID WindID
#1 Germany DE 112000001 256000002
Basically you want to SET NOCOUNT ON at the beginning of your code, and SET NOCOUNT OFF just before the final SELECT statement.

Since database server handles query correctly, save the multiple line action TSQL query as a SQL Server Stored Procedure and have R call it retrieving the resultset.
Do note you can even pass parameters in the EXEC sp line from R to MSSQL. Also as mentioned, include the SET NOCOUNT ON declaration in the query to avoid undesired result character(0):
library("RODBC");
conn <- odbcConnect("DSN Name",uid="***",pwd="***"); # WITH DSN
#conn <-odbcDriverConnect('driver={SQL Server};server=servername;database=databasename;
#trusted_connection=yes;UID=username; PWD=password') # WITH DRIVER
df<-sqlQuery(conn, "EXEC dbo.StoredProcName");

Related

SQL Server Query failing when executed from R

I have the following simple SQL server code:
set nocount on;
if OBJECT_ID('tempdb..#A') IS NOT NULL DROP TABLE #A;
set nocount on;
create table #A
( obj_id int,
obj_name varchar(50),
obj_dt datetime);
insert into #A (
obj_id,
obj_name,
obj_dt)
values
( 1
,'name'
,'2019-01-01 00:00:00'
),
( 2
,NULL
,NULL
),
( 2
,'alias'
,'2019-02-01 00:00:00'
);
set nocount on;
if OBJECT_ID('tempdb..#B') IS NOT NULL DROP TABLE #B;
set nocount on;
select
#A.obj_id
,subq.obj_name
,subq.obj_dt
into #B
from #A
join (select
obj_id,
max(obj_name) as obj_name,
max(obj_dt) as obj_dt
from #A
group by obj_id) as subq
on #A.obj_id = subq.obj_id;
set nocount on;
select * from #B;
which, as expected, returns the following data when executed in Microsoft SQL Server Management Studio:
obj_id obj_name obj_dt
1 name 2019-01-01 00:00:00.000
2 alias 2019-02-01 00:00:00.000
2 alias 2019-02-01 00:00:00.000
So far so good. Now I wish to run this code from R and have that same output returned to R. I have the query above stored in the string query and my RODBC connection stored in the variable connection, and attempt to retrieve the data with
sqlQuery(connection,query)
The result is character(0). However, if I modify the query above by commenting out the subq.obj_name and subq.obj_dt fields in the definition of #B then the code successfully returns the expected dataset
obj_id
1 1
2 2
3 2
from R.
So what is going on here? Both sql queries are valid and run successfully in the Microsoft SQL server environment, but only one works when piped through R. I can't figure out what accounts for the failure of the RODBC code to handle the second query.
It's a known issue about local temporary table (#mytable not ##mytable) and not only in R but all external calls of temporary table even in Microsoft Tools like SSMS (see the comment of the 1st link below).
Look at those links:
https://github.com/r-dbi/odbc/issues/127 and in particular https://github.com/r-dbi/odbc/issues/127#issuecomment-396343426
RODBC Temporary Table Issue when connecting to MS SQL Server
After reading those links It's therefore strange that it does work without subq.obj_name and subq.obj_dt: maybe it works because the queries are in a unique call.
Okay, so I think I've figured out what is going wrong here. The subquery
select
obj_id,
max(obj_name) as obj_name,
max(obj_dt) as obj_dt
from #A
group by obj_id
produces a hidden warning. You don't see the warning if you just run the code as is, but if you store the output in a temporary table then the warning message is produced:
select
obj_id,
max(obj_name) as obj_name,
max(obj_dt) as obj_dt
into #C
from #A
group by obj_id
Warning: Null value is eliminated by an aggregate or other SET
operation.
The warning is hidden when this is run as part of the subquery in the original SQL code in the question. I believe this message is somehow part of output that R is "seeing", and once R sees that output it terminates the query. But since no results have been returned yet the output in R is empty (i.e. character(0)).
To solve this issue I coalesced the variables that I'm computing the max of to some minimal values (I'm not sure about what the minimal character is in sql server collation but '0' worked for my purposes). The idea is to remove NULL values before aggregation so no warning is generated. The final working SQL code is below:
set nocount on;
if OBJECT_ID('tempdb..#A') IS NOT NULL DROP TABLE #A;
set nocount on;
create table #A
( obj_id int,
obj_name varchar(50),
obj_dt datetime);
insert into #A (
obj_id,
obj_name,
obj_dt)
values
( 1
,'name'
,'2019-01-01 00:00:00'
),
( 2
,NULL
,NULL
),
( 2
,'alias'
,'2019-02-01 00:00:00'
);
set nocount on;
if OBJECT_ID('tempdb..#B') IS NOT NULL DROP TABLE #B;
set nocount on;
select
#A.obj_id
,subq.obj_name
,subq.obj_dt
into #B
from #A
join
(select
obj_id,
max(isnull(obj_name,'0')) as obj_name,
max(isnull(obj_dt,cast(-1 as datetime))) as obj_dt
from #A
group by obj_id) as subq
on #A.obj_id = subq.obj_id;
set nocount on;
select * from #B;
I believe this behavior should be addressed in the RODBC package as it is quite likely to trip up others and can be a bit tricky to track down the root cause and troubleshoot.
As an expansion on Rookatu's answer you can turn off the warnings by adding the below to the beginning of your query:
SET ANSI_WARNINGS OFF
Of course this may not be ideal if you are trying to capture other warnings, but can be an easier to implement solution in a pinch.

how to trim trailing spaces in teradata table columns

i want to trim trailing spaces for teradata table columns,
i do it like this,
trim(trailing from dictionary_managed_databases.dbname),
or use trim directly,
trim(dictionary_managed_databases.dbname),
but the result shows:
seems the trim do not work,
not sure how to do it in teradata,
create volatile table test ( dbname varchar(128) CHARACTER SET UNICODE ) on commit preserve rows;
insert into test values ( 'Database-Name' );
-- you don't need to trim a varchar column
select dbname || '~' from test;
(dbname||'~')
---------------------------------------------------------------------------------------------------------------------------------
Database-Name~
-- it is always max length, so not to loose any possible content
select trim(dbname) || '~' from test;
(Trim(BOTH FROM dbname)||'~')
---------------------------------------------------------------------------------------------------------------------------------
Database-Name~
-- you may cast it to shorten the resulting column
select cast(trim(dbname) as varchar(30)) from test;
Trim(BOTH FROM dbname)
------------------------------
Database-Name
-- it will never be less then the header, even if the content is less
select cast(trim(dbname) as varchar(10)) from test;
Trim(BOTH FROM dbname)
----------------------
Database-N
-- but it will truncate the result
select cast(trim(dbname) as varchar(10)) as dbname from test;
dbname
----------
Database-N
sel
dictionary_object_map.moId,
trim(dictionary_managed_databases.dbname)|| '~',
dictionary_deployed_info.dictionaryId,
dictionary_deployed_info.dictionaryName,
dictionary_managed_objects.moname
from dictionary_object_map,
dictionary_deployed_info,
dictionary_managed_objects ,
dictionary_managed_databases
where
dictionary_object_map.dictionaryId=dictionary_deployed_info.dictionaryId
and dictionary_object_map.moid=dictionary_managed_objects.moid
and dictionary_managed_databases.moDBId=dictionary_managed_objects.moDBId
and dictionary_managed_databases.dbname = 'customerservice';
the result
don't understand why output of field dbname still look like this,

Error in Insert function in postgresql

CREATE OR REPLACE FUNCTION InsertInformation
(
p_Name varchar(20)
,p_Address varchar(250)
,p_Mobile int
) RETURNS VOID
as $$
begin
declare v_ID int;
BEGIN
select coalesce(max(Id),0) into v_ID from Information
set; v_ID=v_ID+1
insert into Information
(
Id
,Name
,Address
,Mobile
)
values
(
v_ID
,p_Name
,p_Address
,p_Mobile
)
select v_ID;
$$
LANGUAGE plpgsql;
I convert my sql insert sp to Postgres function using online converter tool but it showing the below mention error
error showing : ERROR: syntax error at or near "insert"
LINE 16: insert into Information
This:
select coalesce(max(Id),0) into v_ID from Information
set; v_ID=v_ID+1
Is wrong.
The select isn't properly terminated, and the set itself is illegal syntax.
You probably want this:
select coalesce(max(Id),0)
into v_ID
from Information; --<< terminate with a ; here
v_id := v_id + 1; --<< terminate with a ; here
But the extra assignment isn't necessary in the first place. The above can be shortened to:
select coalesce(max(Id),0) + 1
into v_ID
from Information;
This
select v_ID;
is also wrong. To return a value use:
return v_id;
But your function is defined as returns void so you can't return anything in the first place.
But: using select coalesce(max(Id),0) + 1 to generate unique IDs is wrong and will not work correctly in a real world application.
The only correct, scalable and fast way to generate new ids is to use a sequence. Really.
The complete function (if you want to return the newly "generated" id) would look like this:
CREATE OR REPLACE FUNCTION InsertInformation(p_Name varchar(20),p_Address varchar(250),p_Mobile int)
RETURNS integer
as
$$
declare
v_ID int;
BEGIN
select coalesce(max(Id),0) + 1
into v_ID
from Information;
INSERT INTO information
(id, name, address, mobile)
VALUES
(v_id, p_name, p_address, p_mobile);
return v_id;
END;
$$
LANGUAGE plpgsql;
SQLFiddle example: http://sqlfiddle.com/#!15/4ac27/1

Entity Framework shows error when called stored procedure

In my project EF calls a stored procedure which is shown below. It returns either 1 or scope identity.
On EF function imports, the stored procedure is listed with a return type of decimal.
When the stored procedure returns scope identity, everything is ok.
But when if condition of sp satisfies, ef throws error as
The data reader returned by the store data provider does not have enough columns for the query requested.
Pls help..
This is my stored procedure:
#VendorId int,
#ueeareaCode varchar(3),
#TuPrfxNo varchar(3),
#jeeSfxNo varchar(4),
#Tjode varchar(3),
#uxNo varchar(3),
#TyufxNo varchar(4),
#Iyuy bit
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
SET NOCOUNT ON;
IF EXISTS (Select dfen_id
from dbo.efe_phfedwn_eflwn
where
[yu] = #Tyuode and
[uy] = #TuyxNo and
[yuno] = #Tuo)
return 1
ELSE
Begin
INSERT INTO dbo.yu
....................
Select Scope_Identity()
End
END
The error tells us that EF is expecting a result set and when we use RETURN we don't get a result set. Your error means that the stored procedure is returning an integer but EF is expecting a decimal, so we just CAST the selected values to a decimal.
So modify the SQL so that we SELECT instead of RETURN, like so (not forgetting to use CAST):
IF EXISTS (Select cntct_ctr_phn_ln_id
from dbo.cntct_ctr_phn_ln
where
[toll_free_phn_area_cd] = #TollfreeareaCode and
[toll_free_phn_prfx_no] = #TollfreePrfxNo and
[toll_free_phn_sfx_no] = #TollfreeSfxNo)
SELECT CAST(1 AS decimal)
Then also CAST the result of SCOPE_IDENTITY() to a decimal:
SELECT CAST(SCOPE_IDENTITY() AS decimal)

Error in stored procedure with string return type

ALTER PROCEDURE [dbo].[Signin_Check]
#uid varchar(50), #pwd varchar(50), #uname varchar(50) output
AS
IF EXISTS (SELECT * FROM threeLayer_user WHERE uid = #uid and pass = #pwd)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT #uname = name
FROM threeLayer_user
WHERE uid = #uid and pass = #pwd
RETURN #uname
END
This stored procedure is throwing an error :
Conversion failed when converting the varchar value 'Saurav' to data type int.
Your code contains the line
return #uname
the return statement can only accept integer values. However, your #uname parameter is an output parameter, so you do not need to also return it. Just make sure that you specify OUTPUT when calling the sproc:
EXEC dbo.Signin_Check #uid, #password, #uname OUTPUT;
The SP doesn't seem to have bad code related to the error, could you check well the parameters being passed to the stored procedure (i.e. their order and their type)? The problem can be the values that you are passing and not the actual procedure.
Could you append to your post the code you are using when you invoke the stored procedure?
Are you sure that it's varchar column?
#uid varchar(50) / int
uid = #uid

Resources