Importing russian characters from Sql Server to R dataframe - sql

I'm using this code:
library(RODBC)
sql3 <- "SELECT TOP 10 Address AS Addr
FROM dbo.Address
Where CountryCode = 'RU'"
con <- odbcDriverConnect('driver={SQL SERVER};server=servername;database=databasename;trusted_connection=true')
df <- as.data.frame(sqlQuery(con,sql3),stringsASFactors=FALSE)
print(df)
Which produces the following results:
> print(df)
Addr
1 115573, ??????, ???????? ?-?, ?.22?
2 107113 ??????, ????? ???????????, ??? 26
3 142200 ??, ?.????????, ??????????? ?., ?. 1
4 614022 ?????, ?????????????? ?????, ??. ????, 37?
5 109453 ?. ?????? ????????????? ????????, ?. 19, ???. 2
6 129282 ??????, ??. ???????, ?.13-?
7 603000 ?????? ????????, ??????? ????????, 2
8 103164 ??????, ????? ??????? ???????????, ??? 26
9 197341, ?????-?????????, ??-? ???????????, ?.19, ????.2
10 429950, ?????????? ???????, ?. ??????????????, ??. ??????????, 42?
The results should be a list of Russian addresses.
As you can maybe see, all 'regular' characters are getting imported fine (e.g. numbers), but the Russian characters aren't making it. I'm guessing I somehow need to set the character encoding before it reaches the dataframe, but I'm not sure how to do that. Also to clarify, the correct address characters appear when the data is queried from SSMS.
Any pointers would be appreciated, thanks.

The ? will be returned for non-english characters unless you specify N forcing unicode.
SELECT 'ук ферт хер' --returns ?? ???? ???
SELECT N'ук ферт хер' --returns ук ферт хер
Most importantly, this has to be done on the insert...
drop table #country
create table #country (names nvarchar(50))
insert into #country(names) values (N'Россия'),('Россия')
SELECT names FROM #country
--Results
--------------------------
names
--------------------------
Россия
??????
-------------------------

Related

How to select a range of columns in a case statement in proc SQL?

I have around 80 columns names diag1 to diag80. I am wondering how can I pick just 30 columns and apply a case statment in proc SqL. The following code produces an error because it doesn't understand the range.
proc sql;
create table data_WANT as
select *,
case
when **diag1:diag30** in ('F00','G30','F01','F02','F03','F051') then 1
else 0
end as p_nervoussystem
from data_HAVE;
quit;
Thank you, any help is appreciated!
You have two problem with that attempted syntax. First is that variable lists are not supported by PROC SQL (since they are not supported by SQL syntax). The second is there is no simple syntax to search N variables for a list of M strings.
You will need a loop of some kind. It will be much easier in SAS code than in SQL.
For example you could make an array to reference your 30 variables than loop over the variables checking whether each one has a value in the list of values. You can stop checking once one is found.
data want;
set have;
array vars diag1-diag30;
p_nervoussystem=0;
do index=1 to dim(vars) while (not p_nervoussystem);
p_nervoussystem = vars[index] in ('F00','G30','F01','F02','F03','F051');
end;
run;
The inverse pattern to #Tom search for a nervous system diagnostic code:
via FINDW over a concatenation of the observed diagnoses
via WHICHC over an array of the observed diagnoses
data have;
infile datalines missover;
length id 8;
array dx(30) $5;
input id (dx1-dx50) (50*:$5.);
datalines;
1 A00 B00 A12
2 F00 Z12 T45
3 A01 A02 B12 F00
4 Q12
5 Q13
6 T14
7 F44 F45 F46
8 . . . . . . . . . . . . . . G30
;
data want;
length p_nervoussystem p_ns 4;
set have;
array dx dx:;
array ns(6) $5 _temporary_ ('F00','G30','F01','F02','F03','F051');
dx_catx = catx(' ', of dx(*));* drop dx_catx; * way 1;
do _n_ = 1 to dim(ns) until(p_nervoussystem);
p_nervoussystem = 0 < indexw(dx_catx, trim(ns(_n_))); * way 1;
p_ns = 0 < whichc(ns(_n_), of dx(*)); * way 2;
end;
run;```
try it sys.tables and sys.columns and filter your columns.
SELECT * FROM sys.tables INNER JOIN sys.columns ON columns.object_id = tables.object_id

SQLRPGLE Cannot insert record - Cast error trying to insert packed decimal into integer field

I'm having an issue with an SQLRPGLE program which must insert records in a table.
I've been debugging my program with strdbg command and I found out that the problem was caused by a packed decimal field called Mes in my data structure, when trying to insert that value into an integer field.
Source data structure definition (the first field in the DS is the field which hypotetically needs casting):
D DataDS DS QUALIFIED TEMPLATE
D Mes 6P 0
D Unidad 2P 0
D Subunidad 3P 0
D Grupopas 8P 0
D Productor 8P 0
D Asegurado 9P 0
. . .
. . .
. . .
The destination field is an integer.
P Exportar...
P B
D PI
D data DS LIKEDS(DataDS)
/free
ClrBI();
CLEAR data;
EXEC SQL DECLARE B1 CURSOR FOR
SELECT MES,UNIDAD, SUBUNIDAD, GRUPOPAS,
PRODUCTOR, ASEGURADO, RAMA, TIPO_MOVIM,
SUCURSAL,IFNULL(FACULTATIV,' '), CONDIC_IVA,
UNIDAD_FC, SUBUNID_FC, GRUPOPR_FC,
MATRICULA, CANALCOBRO, IFNULL(CANALCOBRX,' '),
PRIMACOB, PREMIOCOB, DEREMICOB,
RECADMCOB, RECFINCOB, IVACOB,
PER_IVACOB, ACR_IVACOB, ISSCOB,
INTERNOCOB, PER_IBRCOB, COMISICOBR,
COMISIAGEN,COMISIORGA,COMISIOTRS
COMISITOT
FROM BICOBRANZA
WHERE MES = :mes;
EXEC SQL OPEN B1;
EXEC SQL FETCH NEXT FROM B1 INTO :data;
DOW SQLCOD = 0;
SetBI(data);
CLEAR data;
EXEC SQL FETCH NEXT FROM B1 INTO :data;
ENDDO;
EXEC SQL CLOSE B1;
/end-free
P E
This is the first time I see an error like this.
Does anyone faced this problem before? I don't know even where to start.
Thanks in advance.
A six digit number with no decimal digits (6P 0) has a value range of -999999 to 999999.
An small integer column has a value range of -32768 to 32767..
Casting will work just fine, as long as your packed field value fits.
You'll need to have the DB column be a large (4 byte (10 digits)) or big ( 8 bytes (20 digits)) integer.

Use SAS macro variable to create variable name in PROC SQL

I'm trying to create a set of flags based off of a column of character strings in a data set. The string has thousands of unique values, but I want to create flags for only a small subset (say 10). I'd like to use a SAS macro variable to do this. I've tried many different approaches, none of which have worked. Here is the code that seems simplest and most logical to me, although it's still not working:
%let Px1='12345';
PROC SQL;
CREATE TABLE CLAIM1 AS
SELECT
b.MEMBERID
, b.ENROL_MN
, CASE WHEN (a.PROCEDURE = &Px1.) THEN 1 ELSE 0 END AS CPT_+&Px1.
, a.DX1
, a.DX2
, a.DX3
, a.DX4
FROM ENROLLMENT as b
left join CLAIMS as a
on a.MEMBERID = b.MEMBERID;
QUIT;
Obviously there is only one flag in this code, but once I figure it out the idea is that I would add additional macro variables and flags. Here is the error message I get:
8048 , CASE WHEN (PROCEDURE= &Px1.) THEN 1 ELSE 0 END AS CPT_+&Px1.
-
78
ERROR 78-322: Expecting a ','.
It seems that the cause of the problem is related to combining the string CPT_ with the macro variable. As I mentioned, I've tried several approaches to addressing this, but none have worked.
Thanks in advance for your help.
Something like this normally requires dynamic sql (although I am not sure how will that works with SAS, I believe it may depend on how you have established connection with the database).
Proc sql;
DECLARE #px1 varchar(20) = '12345'
,#sql varhcar(max) =
'SELECT b.MEMBERID
, b.ENROL_MN
, CASE WHEN (a.PROCEDURE = ' + #Px1 + ') THEN 1 ELSE 0
END AS CPT_' + #px1 + '
, a.DX1
, a.DX2
, a.DX3
, a.DX4
FROM ENROLLMENT as b
left join CLAIMS as a
on a.MEMBERID = b.MEMBERID'
EXEC sp_excutesql #sql;
QUIT;
Your issue here is the quotes in the macro variable.
%let Px1='12345';
So now SAS is seeing this:
... THEN 1 ELSE 0 END AS CPT_+'12345'
That's not remotely legal! You need to remove the '.
%let Px1 = 12345;
Then add back on at the right spot.
CASE WHEN a.procedure = "&px1." THEN 1 ELSE 0 END AS CPT_&px1.
Note " not ' as that lets the macro variable resolve.
If you have a list it might help to put the list into a table. Then you can use SAS code to generate the code to make the flag variables instead of macro code.
Say a table with PX code variable.
data pxlist;
input px $10. ;
cards;
12345
4567
;
You could then use PROC SQL query to generate code to make the flag variable into a macro variable.
proc sql noprint;
select catx(' ','PROCEDURE=',quote(trim(px)),'as',cats('CPT_',px))
into :flags separated by ','
from pxlist
;
%put &=flags;
quit;
Code looks like
PROCEDURE= "12345" as CPT_12345,PROCEDURE= "4567" as CPT_4567
So if we make some dummy data.
data enrollment ;
length memberid $8 enrol_mn $6 ;
input memberid enrol_nm;
cards;
1 201612
;
data claims;
length memberid $8 procedure $10 dx1-dx4 $10 ;
input memberid--dx4 ;
cards;
1 12345 1 2 . . .
1 345 1 2 3 . .
;
We can then combine the two tables and create the flag variables.
proc sql noprint;
create table want as
select *,&flags
from ENROLLMENT
natural join CLAIMS
;
quit;
Results
memberid procedure dx1 dx2 dx3 dx4 enrol_mn CPT_12345 CPT_4567
1 12345 1 2 201612 1 0
1 345 1 2 3 201612 0 0

Ckeck the presence of xml tag using oracle

<wbi:appData>
<wbi:content wbi:name="1st_status">
<wbi:value xsi:type="xsd:string">Success</wbi:value>
</wbi:content>
</wbi:appData>
this xml is in a table which has a column in the form of CLOB type.
I wanted to find if "wbi:value" tag exists in this xml or not ?
I tried using existsnode but in sql developer it is saying an error as to declare existsnode.
yes use existsnode:
SQL> with yourdata as (select to_clob('<wbi:event xmlns:wbi="http://foo" xmlns:xsi="http://x" xmlns:xsd="http://d">
2 <wbi:appData>
3 <wbi:content wbi:name="1st_status">
4 <wbi:value xsi:type="xsd:string">Success</wbi:value>
5 </wbi:content>
6 <wbi:content wbi:name="2nd_status">
7 <wbi:value xsi:type="xsd:string">Failure</wbi:value>
8 </wbi:content>
9 </wbi:appData>
10 </wbi:event>') c from dual)
11 select existsnode(xmltype(c), '/wbi:event/wbi:appData/wbi:content','xmlns:wbi="http://foo"') is_exist
12 from yourdata t
13 /
IS_EXIST
----------
1
ie
existsnode(xmltype(c), '/wbi:event/wbi:appData/wbi:content','xmlns:wbi="http://foo"')
1 = exists
0 = does not exist.
note that in my sample, i had two matching nodes (as i didn't filter on wbi:name). you can filter the xpath of course. eg:
/wbi:event/wbi:appData/wbi:content[#wbi:name="1st_status"]
to limit matches to the "1st_status" one
select count(*)
from clobtab
where existsNode(xmltype.createxml(clobcol),'/wbi:appData/wbi:content/wbi:value') = 1;
If it reurns more than 0 then it exists otherwise not.
So your trigger would be-
CREATE TRIGGER Tab_a
BEFORE INSERT
FOR EACH ROW
declare
xml_a xmltype;
begin
xml_a:=xmltype(:new.value);
if existsNode(xml_a,'/wbi:appData/wbi:content/wbi:value','xmlns:wbi="http://pat.namespace.com"') = 1
then
----insert ....
end if;
end;
actually you can use oracle's instr function, which is fast.
like:
where instr(field, 'wbi:value') > 0
You can use XMLEXISTS:
SELECT DESCRIPTOR_XML FROM TABLE_WITH_AN_XMLTYPE_COLUMN
WHERE
XMLEXISTS('//functions[function/arg[#name="class.name" and not(starts-with(., "com.example.apps.YouShantSeeMeClass"))]]'
PASSING BY VALUE DESCRIPTOR_XML);

How to create a concatenated string of row values based on flags in SQL Server

I am using SQL Server 2008r2.
Here is what I am trying to accomplish:
I have a table with the design:
Flag Text
________________________
0 'No Error'
1 'Bad Data'
2 'Bad Header'
4 'Unknown error'
My second table is designed:
ID Flags
_______________________
500 0
501 3
502 4
504 6
550 0
The flags in the second table represent a bitwise combination of the flags in the first table (e.g. Flags = 3 is 'Bad Data' AND 'Bad Header', Flags = 6 is 'Bad Header' AND 'Unknown error').
I want a query that will produce the following:
ID ConcatText
____________________________
500 'No Error'
501 'Bad Data, Bad Header'
502 'Unknown error'
504 'Bad Header, Unknown error'
550 'No Error'
What is the best way to achieve this without the use of user-defined functions, or user-defined stored procedures?
Thanks for any help.
This article explains exactly how to accomplish this. It puts it together step by step so that you understand what is going on, too. It basically combines the bitwise operators in SQL, and then the rest is accomplished similar to what hkf posted. Hopefully, this is helpful to you :)
I believe this will translate out to be:
SELECT a.id,
REPLACE(REPLACE(REPLACE(
(
SELECT TEXT
FROM FlagTable AS b
WHERE a.flags & b.flag <> 0
ORDER BY b.text FOR XML Raw
)
, '"/><row value="', ', '), '<row value="', ''), '"/>', '')
AS 'attributes'
FROM FlagMappingTable AS a
ORDER BY a.id;
You need a combination of CROSS APPLY and FOR_XML_PATH()
See Simulating group_concat MySQL function in Microsoft SQL Server 2005?
Oh I LOVE bitwise, truely, not sarcasm. I think this is the simplest. You've got CTE's available to you, I say use 'em!
Try this. I borrowed from Concatenate many rows into a single text string? with my own flavor of a join for bitwise.
*I apologize for mistakes, this is untested and written in Notepad.
WITH lines AS
(
SELECT
row_number() over(order by ID) lineid,
FlagMap.ID
, Flag.Text AS ConcatText
FROM
FlagMap
LEFT JOIN
Flags
ON FlagMap.Flags & Flags.Flag = Flags.Flag
OR (FlagMap.Flags = 0 AND Flag.Flag = 0)
),
result_lines AS
(
SELECT
lineid,
cast(ConcatText as nvarchar(max)) ConcatText
FROM
lines
WHERE
lineid = 1
UNION ALL
SELECT
l.lineid,
cast(r.ConcatText + N', ' + l.ConcatText AS nvarchar(max))
FROM
lines l
INNER JOIN
result_lines r
on
l.lineid = r.lineid + 1
)
SELECT
ID
, ConcatText
FROM
result_lines
ORDER BY
ID DESC