Using Excel File for Variables in Qlikview says File not found - subroutine

In my company we are using an excel file to store our expressions and variables so we can update them in one central location and we use multiple tabs (in qlikview tables) to keep them organized. I didn't like the duplication of code, so I created a sub procedure and then just call it with the variables that change. the syntax looks correct but it keeps giving me a script Error that says:
Field not Found = <,>
VariableList:
Load ,
From [D:\Development\UserDocs\ExpressionDictionary.xlsx] (ooxml,
embedded labels, table is $(ExcelTable)) WHERE [Load Flag];
sub GetVariables(TableName, ExcelTable, NameColumn, VariableColumn)
$(TableName):
LOAD
$(NameColumn),
$(ExpressionColumn)
FROM [D:\Development\UserDocs\ExpressionDictionary.xlsx] (ooxml, embedded labels, table is $(ExcelTable))
WHERE [Load Flag];
FOR counter = 1 TO NoOfRows('$(TableName)')
LET vVariable = Peek('$(NameColumn)', $(counter) -1, '$(TableName)');
LET $(vVariable) = Peek('$(ExpressionColumn)', $(counter) -1, '$(TableName)');
NEXT
LET vVariable = null();
LET counter = null();
end sub
call GetVariables('VariableList', 'Variables')
I understand that it doesn't see the column Names but I don't know how to get it to work. I have tried both with and without ' surrounding the dollar expansion in the Load script. What am I missing?

The GetVariables function you have there takes 4 parameters - TableName, ExcelTable, NameColumn and VariableColumn.
When you've called it, you've only specified the first 2, so NameColumn and Variable Column are blank. To fix it you need to tell it what the column names from the Variables sheet in Excel you want it to load. Could be "VariableName" and "VariableValue" but I can't say without seeing the file itself.
Something like:
call GetVariables('VariableList','Variables','VariableName','VariableValue')
The error is because it's running this code:
VariableList:
LOAD
,
FROM [D:\Development\UserDocs\ExpressionDictionary.xlsx]
(ooxml, embedded labels, table is Variables)
WHERE [Load Flag];
Note that there are no field names in the LOAD statement as these variable values are not defined.

Related

Selecting variables in a dataset according to values in another dataset

I want to create a subset for a dataset which has around 100 variables and I wish to KEEP only those variables that are present as values of another variable in another dataset. Can someone pleae help me with a SPSS Syntax.
This is what it should look like:
DATASET ACTIVATE basedataset.
SAVE OUTFILE ='Newdata.sav'
/KEEP Var1.
Var 1 is the variable in the other dataset which contains all the values based on which i want to perform the subsetting.I am not sure if vector should be involved or if there is an easier way to do this.
The following will create a macro containing the list of variables you require, to use in your analysis or in subsetting the data.
First I'll create some sample data to demonstrate on:
data list free /v1 to v10 (10f3).
begin data
1,2,3,2,4,7,77,777,66,55
end data.
dataset name basedataset.
data list free/var1 (a4).
begin data
"v3", "v5", "v6", "v9"
end data.
dataset name varnames.
Now to create the list:
dataset activate varnames.
write out="yourpath\var1 selection.sps"
/"VARIABLE ATTRIBUTE VARIABLES= ", var1, " ATTRIBUTE=selectVars('yes')." .
exe.
dataset activate basedataset.
VARIABLE ATTRIBUTE VARIABLES=all ATTRIBUTE=selectVars('no').
insert file="yourpath\var1 selection.sps".
SPSSINC SELECT VARIABLES MACRONAME="!varlist" /ATTRVALUES NAME=selectVars VALUE = yes .
The list is now ready and can be called using the macro name !varlist in any command, for example:
freq !varlist.
or
SAVE OUTFILE ='Newdata.sav' /KEEP !varlist.

Error 1004 - Vlookup in vba - Unable to get the Vlookup property of the WorksheetFunction class

I've browsed the various questions already asked with this issue other users have faced and none of the solutions seem to fix the error code coming up.
I have a form which prompts the user for a reference number - they input this into the text field and then press OK
'OK button from form1
Public Sub CommandButton1_Click()
refInput = refTextBox.Value
InputRef.Hide
ExportShipForm.Show
End Sub
Once this has been pressed, the next form appears which I would like to be populated with data based on the reference number input on the first form. I have an update button which will update the "labels" on the form to show the data - this is where I am getting an error.
The first label to update is through a Vlookup:
Below the users clicks the update button the 2nd form:
Public Sub btnUpdate_Click()
Call ICS_Caption
lbl_ICS.Caption = Label_ICS
End Sub
This calls a function below:
Public Sub ICS_Caption()
Dim ws1 As Worksheet
refInput = InputRef.refTextBox.Value
Set ws1 = Worksheets("MACRO")
dataRef = Worksheets("Shipping Data").Range("A:K")
Label_ICS = WorksheetFunction.VLookup(refInput, dataRef, 7, False)
End Sub
The error continues to come up each time - I have ran the vlookup manually in a cell outside of VBA and it works fine.
I have typed the range in the Vlookup whilst also using named ranges but each variation shows the same error.
Eventually, I would want the label on form 2 to update with the result of the Vlookup.
Any ideas?
You need to Dim dataRef as Range and then Set it.
See code Below:
Dim DataRef as Range
Set dataRef = Worksheets("Shipping Data").Range("A:K")
Just like a Workbook or Worksheet you need to Set the Range
Just as Grade 'Eh' Bacon suggest in comments its always best to Dim every reference.
The best way to do so is to put Option Explicit all the way at the top of your code. This forces you to define everything which helps it preventing mistakes/typo's etc.
Update edit:
The problem was you are looking for a Reference number in your Sheet (thus an Integer) but refInput is set as a String this conflicts and .VLookup throws an error because it can't find a match.
I have reworked your code:
Your sub is now a function which returns the .Caption String
Function ICS_Caption(refInput As Integer)
Dim dataRef As Range
Set dataRef = Worksheets("Shipping Data").Range("A:K")
ICS_Caption = WorksheetFunction.VLookup(refInput, dataRef, 7, False)
End Function
The update Button calls your Function and provides the data:
Public Sub btnUpdate_Click()
lbl_ICS.Caption = ICS_Caption(InputRef.refTextBox.Value)
End Sub
By using a Function you can provide the Integer value and get a return value back without the need of having Global Strings or Integers.
Which would have been your next obstacle as you can only transfer Variables between Modules/Userforms by using a Global Variable.
I would even advice to directly use the function in the Initialize Event of your 2nd Userform to load the data before the Userform shows this is more user friendly then needing to provide data and then still needing to push an update button.
Verify that you have no missing libraries in VBA IDE > Tools > References
Try using a worksheet cell as the place to store and retrieve refTextBox.Value, rather than refInput (which I assume is a global variable):
Public Sub CommandButton1_Click()
...
Worksheets("Shipping Data").Range($M$1).Value=refTextBox.Value
End Sub
Public Sub ICS_Caption()
Dim refInput as Long'My assumption
...
refInput=Worksheets("Shipping Data").Range($M$1).Value
...
End Sub
Make sure you have Option Explicit at the top of all of your code windows.

Assigning a whole DataStructure its nullind array

Some context before the question.
Imagine file FileA having around 50 fields of different types. Instead of all programs using the file, I tried having a service program, so the file could only be accessed by that service program. The programs calling the service would then receive a DataStructure based on the file structure, as an ExtName. I use SQL to recover the information, so, basically, the procedure would go like this :
Datastructure shared by service program :
D FileADS E DS ExtName(FileA) Qualified
Procedure called by programs :
P getFileADS B Export
D PI N
D PI_IDKey 9B 0 Const
D PO_DS LikeDS(FileADS)
D LocalDS E DS ExtName(FileA) Qualified
D NullInd S 5i 0 Array(50) <-- Since 50 fields in fileA
//Code
Clear LocalDS;
Clear PO_DS;
exec sql
SELECT *
INTO :LocalDS :nullind
FROM FileA
WHERE FileA.ID = :PI_IDKey;
If SqlCod <> 0;
Return *Off;
EndIf;
PO_DS = LocalDS;
Return *On;
P getFileADS E
So, that procedure will return a datastructure filled with a record from FileA if it finds it.
Now my question : Is there any way I can assign the %nullind(field) = *On without specifying EACH 50 fields of my file?
Something like a loop
i = 1;
DoW (i <= 50);
if nullind(i) = -1;
%nullind(datastructure.field) = *On;
endif;
i++;
EndDo;
Cause let's face it, it'd be a pain to look each fields of each file every time.
I know a simple chain(n) could do the trick
chain(n) PI_IDKey FileA FileADS;
but I really was looking to do it with SQL.
Thank you for your advices!
OS Version : 7.1
First, you'll be better off in the long run by eliminating SELECT * and supplying a SELECT list of the 50 field names.
Next, consider these two web pages -- Meaningful Names for Null Indicators and Embedded SQL and null indicators. The first shows an example of assigning names to each null indicator to match the associated field names. It's just a matter of declaring a based DS with names, based on the address of your null indicator array. The second points out how a null indicator array can be larger than needed, so future database changes won't affect results. (Bear in mind that the page shows a null array of 1000 elements, and the memory is actually relatively tiny even at that size. You can declare it smaller if you think it's necessary for some reason.)
You're creating a proc that you'll only write once. It's not worth saving the effort of listing the 50 fields. Maybe if you had many programs using this proc and you had to create the list each time it'd be a slight help to use SELECT *, but even then it's not a great idea.
A matching template DS for the 50 data fields can be defined in the /COPY member that will hold the proc prototype. The template DS will be available in any program that brings the proc prototype in. Any program that needs to call the proc can simply specify LIKEDS referencing the template to define its version in memory. The template DS should probably include the QUALIFIED keyword, and programs would then use their own DS names as the qualifying prefix. The null indicator array can be handled similarly.
However, it's not completely clear what your actual question is. You show an example loop and ask if it'll work, but you don't say if you had a problem with it. It's an array, so a loop can be used much like you show. But it depends on what you're actually trying to accomplish with it.
for old school rpg just include the nulls in the data structure populated with the select statement.
select col1, ifnull(col1), col2, ifnull(col2), etc. into :dsfilewithnull where f.id = :id;
for old school rpg that can't handle nulls remove them with the select statement.
select coalesce(col1,0), coalesce(col2,' '), coalesce(col3, :lowdate) into :dsfile where f.id = :id;
The second method would be easier to use in a legacy environment.
pass the key by value to the procedure so you can use it like a built in function.
One answer to your question would be to make the array part of a data structure, and assign *all'0' to the data structure.
dcl-ds nullIndDs;
nullInd Ind Dim(50);
end-ds;
nullIndDs = *all'0';
The answer by jmarkmurphy is an example of assigning all zeros to an array of indicators. For the example that you show in your question, you can do it this way:
D NullInd S 5i 0 dim(50)
/free
NullInd(*) = 1 ;
Nullind(*) = 0 ;
*inlr = *on ;
return ;
/end-free
That's a complete program that you can compile and test. Run it in debug and stop at the first statement. Display NullInd to see the initial value of its elements. Step through the first statement and display it again to see how the elements changed. Step through the next statement to see how things changed again.
As for "how to do it in SQL", that part doesn't make sense. SQL sets the values automatically when you FETCH a row. Other than that, the array is used by the host language (RPG in this case) to communicate values back to SQL. When a SQL statement runs, it again automatically uses whatever values were set. So, it either is used automatically by SQL for input or output, or is set by your host language statements. There is nothing useful that you can do 'in SQL' with that array.

Matlab - using a function mulitple times in the same workspace, to add values and fields to a structure

I have a structure such as:
specimen.trial1 = 1
I now want to add another trial to the specimen, so that
specimen.trial1 = 1
specimen.trial2 = 2
I can do this without a problem within the workspace and command window. But, if I'm using a function to calculate the numbers for each trial (with dynamic fields), the new field and value erases the previous one. Eg:
function [specimen] = dummy(trial,value)
specimen.(trial) = value
end
run the function:
[specimen] = dummy('trial1',1)
then run the function again with different inputs, but keeping the structure intact in the workspace
[specimen] = dummy('trial2',2)
Instead of getting a structure with 2 fields, I get just one with Trial2 being the only field. Does that make any sense? What would like is to use the outputs of a function to progressively add to a structure.
Thank you,
Chris
Yes it makes sense, because you're creating a new struct specimen within your function.
Solution: pass the the previous specimen to the function as well.
function [specimen] = dummy(specimen,trial,value)
specimen.(trial) = value
end
and call:
[specimen] = dummy(specimen,'trial1',1)
or alternativly leave out the assignment at all and use the following
function [output] = dummy(value)
output = value
end
and call:
[specimen.trail1] = dummy(1)
which really depends on what you actually want to do. Put passing a name to a function which uses this name to define a struct is a little pointless unless you "use" that name otherwise. Also if you want to have input-dependent dynamic names you'd also go with the first alternative

Dynamically reference temp-table column values in Progress

I am using Progress 4GL
I have a spreadsheet of data containing several columns called data1....50.
I have created a temp table which holds all the values.
Now I would like to loop through the temp table columns and do various calculations
So I need something like this:
for each record loop thru cols_in_temp_table .
if col_value = "XYZ" then
do calcs and stuff
end.
So how do I reference the temp_table cols ?
Ok, didn't resolve the original query, but found a workaround. Split the data up and put into separate tables, long winded, but does the trick.
Depending on your version, this is one way to do it:
DEFINE VARIABLE h-cols AS HANDLE NO-UNDO.
h-cols = tt-cols:BUFFER-HANDLE.
FOR EACH tt-cols
NO-LOCK:
IF h-cols::col-name = "some value" THEN
RUN do-something.
END.
For versions that can't do the "::" operator, do this:
FOR EACH tt-cols
NO-LOCK:
IF h-cols::buffer-field("col-name"):buffer-value = "some value" THEN
RUN do-something.
END.