Call Incremental Datasets Created by Macro Function - syntax

I have a macro variable called max_attempts I created from a a PROC SQL that equals 4 for my current datafile. Then, I used a macro function to create datasets up to max_attempts so now I have attempt1_table, attempt2_table, attempt3_table, and attempt4_table. Now I'm having trouble merging the 4 datasets.
data final_table;
set attempt1_table - attempt&max_attempts._table;
run;
The inputted datafile will have a different max_n each time, so I'm using macros to account for that.

The - shortcut only works if the number is at the end of the dataset name. Rename your datasets to be round_table1, round_table2, etc.:
data final_table;
set round_table1 - round_table&max_n.;
run;

Use the trimmed option of the into :macrovar clause in order to remove the leading spaces that cause set attempt1_table - attempt&max_attempts._table; to resolve into erroneous syntax.
Example:
proc sql noprint;
select <computation-for-max-attempts>
into :max_attempts trimmed /* removes leading spaces when column is numeric */
from ...
;
quit;

Thank you everyone for your help! It was two issues, the number has to be at the end of the dataset name when using the - shortcut and using trimmed to remove leading spaces.
proc sql feedback;
select max(max_attempts)
into: max_attempts trimmed
from analysis_data;
quit;
data analysis_table;
set unknown_table attempt_table1 - attempt_table&max_attempts;
run;

Related

How can I use 'update where' select in FoxPro?

I am totally new to FoxPro (and quite fluent with MySQL).
I am trying to execute this query in FoxPro:
update expertcorr_memoinv.dbf set 'Memo' = (select 'Memo' from expertcorr_memoinv.dbf WHERE Keymemo='10045223') WHERE Keydoc like "UBOA"
I got the error:
function name is missing )
How can I fix it?
In FoxPro SQL statements you would not 'single-quote' column names. In Visual FoxPro version 9 the following sequence would run without errors:
CREATE TABLE expertcorr_memoinv (keydoc Char(20), keymemo M, Memo M)
Update expertcorr_memoinv.dbf set Memo = (select Memo from expertcorr_memoinv.dbf WHERE Keymemo='10045223') WHERE Keydoc like "UBOA"
If you would provide a few sample data and an expected result, we could see whether the line you posted would do what you want after correcting the single-quoted 'Memo' names.
NB 1: "Memo" is a reserved word in FoxPro.
NB 2: As you know, the ";" semicolon is a line-continuation in Visual FoxPro, so that a longer SQL statement can be full; of; those;
So that the Update one-liner could be written as:
Update expertcorr_memoinv ;
Set Memo = (Select Memo From expertcorr_memoinv ;
WHERE Keymemo='10045223') ;
WHERE Keydoc Like "UBOA"
NB 3: Alternatively, you can SQL Update .... From... in Visual FoxPro, similar to the Microsoft SQL Server feature. See How do I UPDATE from a SELECT in SQL Server?
I would do that just as Stefan showed.
In VFP, you also have a chance to use non-SQL statements which make it easier to express yourself. From your code it feels like KeyMemo is a unique field:
* Get the Memo value into an array
* where KeyMemo = '10045223'
* or use that as a variable also
local lcKey
lcKey = '10045223'
Select Memo From expertcorr_memoinv ;
WHERE Keymemo=m.lcKey ;
into array laMemo
* Update with that value
Update expertcorr_memoinv ;
Set Memo = laMemo[1] ;
WHERE Keydoc Like "UBOA"
This is only for divide & conquer strategy that one may find easier to follow. Other than that writing it with a single SQL is just fine.
PS: In VFP you don't use backticks at all.
Single quotes, double quotes and opening closing square brackets are not used as identifiers but all those three are used for string literals.
'This is a string literal'
"This is a string literal"
[This is a string literal]
"My name is John O'hara"
'We need 3.5" disk'
[Put 3.5" disk into John's computer]
There are subtle differences between them, which I think is an advanced topic and that you may never need to know.
Also [] is used for array indexer.
Any one of them could also be used for things like table name, alias name, file name ... (name expression) - still they are string literals, parentheses make it a name expression. ie:
select * from ('MyTable') ...
copy to ("c:\my folder\my file.txt") type delimited

How can I insert a string in an IO stream in Ruby?

I'm trying to write a Ruby script that will tweak a SQL dump (taken from pg_dump) so it can set up a table cleanly.
So far it's been all good the way I've set it up; I've been able to File.read the file, insert a word, append some stuff to the end, and File.write the file again.
However, I'm now working with a dump that's nearly 7 GB, and it won't cope (File.read is raising EINVAL errors, and there's no trouble with the filename). So I want to use a single stream to find the right spot to insert that word, and then jump to the end and append the extra stuff.
But I can't insert that word. I want to change
DROP TABLE public.programmes;
SET search_path = public, pg_catalog;
to
DROP TABLE public.programmes CASCADE;
SET search_path = public, pg_catalog;
but using file_stream.puts (#write and #<< aren't any better), I end up overwriting part of the following line:
DROP TABLE public.programmes CASCADE;
ch_path = public, pg_catalog;
... and I'd rather not have to loop (read eight characters, seek back eight characters, write previous eight characters) all the way to the end of the file, 7 GB away.
(I might be okay with doing it back to the start of the file – that's only 460 B – but I'd still have to know how to insert some characters at the start.)
Is there a way to do this?
Since the place where CASCADE needed to go was so close to the start, I ended up writing eight characters to the file first, then appending the results of pg_restore. Then I could loop through the file stream from the start and drop the string into place...
# Could be any eight characters, but these are a valid SQL comment in case it fails
File.write(path, "-------\n")
system("pg_restore #{pgr_options} >> #{path}")
File.open(path, 'r+') do |stream|
content = ''
stream.pos = 8
# The semicolon is needed to delimit the table name
content << stream.getc until content =~ /(DROP TABLE public.[a-z_]*);/
stream.rewind
stream << content[0..-2] << ' CASCADE;'
... before jumping to the end and appending stuff.
stream.seek 0, :END
stream.puts "ALTER TABLE ONLY blah blah blah..."
end

How to do double for loop to generate keep list in SAS?

I have a very large dataset with over 1000 columns, with column names formatted like this:
WORLDDATA.table2_usa_2017_population
WORLDDATA.table2_japan_2017_gnp
I only need to keep a subset of these parameters for a select few countries. I specify the custom lists:
%let list1 = usa canada uk japan southafrica;
%let list2 = population crimerate gnp;
How do I do a double for loop like so:
param_list = []
for (i in list1) {
for (j in list2) {
param_name = WORLDDATA.table2_{list1[i]}_2017_{list2[j]}
param_list.append(param_name)
}
}
such that I can use param_list in
data final_dataset;
set WORLDDATA.table2;
keep {param_list};
run;
Thank you!
Your original data set has data items country and topic encoded into the column name (metadata) you will probably need to transpose the data for use in SAS procedure steps that would use statements such as where, by and class.
Proc TRANSPOSE can pivot data from wide to tall and the output will have a column named _NAME_ which can be used in a where=(where-statement) option on the output data set. Th where-statement would be a regex expression having your lists specified as alternation (|) items in a group (such as (item-1|...|item-N)). The regex engine would perform the implicit outer join that the nested loop in the question pseudo code does. The regex pattern would use the /ix modifiers in order to have a pattern formatted for human readability that also ignores case.
In order to have Proc TRANSPOSE pivot each row of a data set, the data set needs to have row key (a variable or variables in combination) that are distinct from row to row.
Untested example:
proc transpose data=have_wide out=want_subset_categorical (where=(
prxmatch("(?ix)/
table2_
%sysfunc(translate(&LIST1.,|,%str( )) (?# list 1 spaces converted to | ors )
_2017_
%sysfunc(translate(&LIST2.,|,%str( )) (?# list 2 spaces converted to | ors )
/",_name_)
));
by <row-key>;
run;

SAS format procedure, invalue statement ,UPCASE option does not work

I need to create SAS informat that will change all case versions of 'Male' and 'Female' to digits.
I found in the documentation that there is UPCASE options that does the job. "converts all raw data values to uppercase before they are compared to the possible ranges. If you use UPCASE, then make sure the values or ranges you specify are in uppercase"
Unfortunately after adding the UPCASE option none of the input values is read properly.
The SAS version id 9.2.
My code is below.
options fmtsearch=(WORK);
proc format lib=WORK;
invalue gender UPCASE
MALE = 1
FEMALE = 2
;run;
data _null_;
q='MALE';
x=input(q,gender.);
put q=;
put x=;
run;
The log is:
NOTE: Invalid argument to function INPUT at line 186 column 7.
q=MALE
x=.
q=MALE x=. _ERROR_=1 _N_=1
What is the proper usage of this option?
Very simple, just put UPCASE inside brackets...

How to set default value for missing substitution parameter

I'm calling a procedure in a script and passing variables like this:
##./procedudure.sql 'var1' 'var2' 'var3'
In the procedure I'm using the values like this:
DEFINE variable1 = '&1'
DEFINE variable2 = '&2'
DEFINE variable3 = '&3'
...
insert into TABLE (id, text) values ('&varibale1', '&variable2&variable3')
I want to be able to call the procedure with only two parameters instead of 3 so that the last one is replaced with empty string. But when I call it like this, I get this prompt:
Enter value for 3:
I've also tried to use the parameters like this, but with the same result:
DEFINE variable2 = '&2' || '&3'
I've found this page for using prompt for missing variable, but couldn't find anywhere how to set a default value: https://blogs.oracle.com/opal/entry/sqlplus_101_substitution_varia#9_14
As you can see from the code, I want the last parameters to be concatenated. This should be a workaround for 240-character limit.
Well yes I see it's old but... until now, unanswered!
OP, this is what you're looking for but it's not exactly simple!
I originally spotted the technique on Tanel Poder's website but not sure if he came up with it or not.
Anyway, here I'm using it to setup defaults in case warning and critical limits are not passed in to the script.
rem setup default values
def DEFAULT_WARN_LEVEL='97'
def DEFAULT_CRIT_LEVEL='99'
rem trick to assign values to &1 and &2 so that SQL*Plus does not ask
col warn_level new_value 1
col crit_level new_value 2
select null warn_level
, null crit_level
from dual
where 1=2
/
rem if values were passed in, use them, if not, use the defaults
select nvl('&1','&DEFAULT_WARN_LEVEL') warn_level
, nvl('&2','&DEFAULT_CRIT_LEVEL') crit_level
from dual
/
rem the values, either passed in or defaults are now in &1 and &2
rem put the values in meaningful variables
def warn_level='&1'
def crit_level='&2'
When you code it as DEFINE variable3 = '&3' in your procedure, the system thinks 3 is variable and it will always prompt for its value.
In case you want to pass a default value for 3, you should do DEFINE 3 = YOUR_VALUE_FOR_PARAM_3
In the example link, section 9.14, I don't see where he uses any default value. He just either asks for it, or takes the parameter. SQLPLUS always is going to ask for any &1, &2, .. he sees in your file, if it is not given as input parameter. No matter how and where you use it. It's not like in Bash, where he just replaces $1 with null, and doesn't do anything. I don't think you can disable the auto-asking in SQLPLUS.

Resources