I am creating a report that the business would like to use to spot check 5, 10 or 20 records at a time.
The request was to use a Search parameter (as opposed to dropdown params).
How can I create a Search parameter that will allow for multiple values separated by a comma?
Thanks for the help.
Don't worry about the length of this answer, most of it is a cut/paste job ! I've tried to explain each bit as we go so you understand it better, the actual amount of code you need to craft is minimal.
If you have SQL Server 2016 then you can take advantage of the new string_split function, if you have an older version you'll have to create a similar function yourself, or copy the one I created a few years back which does a similar thing.
Lets get the function created first: I've created it in the fn schema but you can obviously change this to whatever schema you like.
CREATE FUNCTION [fn].[Split](#sText varchar(8000), #sDelim varchar(20) = ' ')
RETURNS #retArray TABLE (idx smallint Primary Key, value varchar(8000))
AS
BEGIN
DECLARE #idx smallint,
#value varchar(8000),
#bcontinue bit,
#iStrike smallint,
#iDelimlength tinyint
IF #sDelim = 'Space'
BEGIN
SET #sDelim = ' '
END
SET #idx = 0
SET #sText = LTrim(RTrim(#sText))
SET #iDelimlength = DATALENGTH(#sDelim)
SET #bcontinue = 1
IF NOT ((#iDelimlength = 0) or (#sDelim = 'Empty'))
BEGIN
WHILE #bcontinue = 1
BEGIN
--If you can find the delimiter in the text, retrieve the first element and
--insert it with its index into the return table.
IF CHARINDEX(#sDelim, #sText)>0
BEGIN
SET #value = SUBSTRING(#sText,1, CHARINDEX(#sDelim,#sText)-1)
BEGIN
INSERT #retArray (idx, value)
VALUES (#idx, #value)
END
--Trim the element and its delimiter from the front of the string.
--Increment the index and loop.
SET #iStrike = DATALENGTH(#value) + #iDelimlength
SET #idx = #idx + 1
SET #sText = LTrim(Right(#sText,DATALENGTH(#sText) - #iStrike))
END
ELSE
BEGIN
--If you can't find the delimiter in the text, #sText is the last value in
--#retArray.
SET #value = #sText
BEGIN
INSERT #retArray (idx, value)
VALUES (#idx, #value)
END
--Exit the WHILE loop.
SET #bcontinue = 0
END
END
END
ELSE
BEGIN
WHILE #bcontinue=1
BEGIN
--If the delimiter is an empty string, check for remaining text
--instead of a delimiter. Insert the first character into the
--retArray table. Trim the character from the front of the string.
--Increment the index and loop.
IF DATALENGTH(#sText)>1
BEGIN
SET #value = SUBSTRING(#sText,1,1)
BEGIN
INSERT #retArray (idx, value)
VALUES (#idx, #value)
END
SET #idx = #idx+1
SET #sText = SUBSTRING(#sText,2,DATALENGTH(#sText)-1)
END
ELSE
BEGIN
--One character remains.
--Insert the character, and exit the WHILE loop.
INSERT #retArray (idx, value)
VALUES (#idx, #sText)
SET #bcontinue = 0
END
END
END
RETURN
END
Once the function is created you can see what it outputs by doing something like
select * from fn.Split('Austria, Belgium, France', ',')
This returns the following
idx value
0 Austria
1 Belgium
2 France
Lets assume we have a geography table with the names of countries and their associated region, we can search for matching entries by simply joining to the output of the function something like this.
select g.CountryID, g.CountryDesc, g.ContinentDesc from dim.Geography g
join (SELECT * FROM fn.Split('Austria, Belgium, France', ',')) s
on g.CountryDesc = s.value
This, in my case, give me this output.
CountryID CountryDesc ContinentDesc
21 Austria West Europe
28 Belgium West Europe
89 France West Europe
To use the split function in your SSRS dataset, simply pass in the search text parameter so the query would now look something like this.
select g.CountryID, g.CountryDesc, g.ContinentDesc from dim.Geography g
join (SELECT * FROM fn.Split(#MySearchText, ',')) s
on g.CountryDesc = s.value
Related
I'm quite new to Matlab and I'm struggling trying to figure out how to properly preprocess my data in order to make some calculations with it.
I have an Excel table with financial log returns of many companies such that every row is a day and every column is a company:
I imported everything correctly into Matlab like this:
Now I have to create what's caled "rolling windows". To do this I use the following code:
function [ROLLING_WINDOWS] = setup_returns(RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
for i = 1:limit_rows
ROLLING_WINDOWS(i).SYS = RETURNS(i:bandwidth+i-1,1);
end
end
Well if I run this code for the first column of returns everything works fine... but my aim is to produce the same thing for every column of log returns. So basically I have to add a second for loop... but what I don't get is which syntax I need to use in order to make that ".SYS" dynamic and based on my array of string cells containing company names so that...
ROLLING_WINDOWS(i)."S&P 500" = RETURNS(i:bandwidth+i-1,1);
ROLLING_WINDOWS(i)."AIG" = RETURNS(i:bandwidth+i-1,2);
and so on...
Thanks for your help guys!
EDIT: working function
function [ROLLING_WINDOWS] = setup_returns(COMPANIES, RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
for i = 1:limit_rows
offset = bandwidth + i - 1;
for j = 1:columns
ROLLING_WINDOWS(i).(COMPANIES{j}) = RETURNS(i:offset, j);
end
end
end
Ok everything is perfect... just one question... matlab intellissense tells me "ROLLING_WINDOWS appears to change size on every loop iteration bla bla bla consider preallocating"... how can I perform this?
You're almost there. Use dynamic field names by building strings for fields. Your fields are in a cell array called COMPANIES and so:
function [ROLLING_WINDOWS] = setup_returns(COMPANIES, RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
%// Preallocate to remove warnings
ROLLING_WINDOWS = repmat(struct(), limit_rows, 1);
for i = 1:limit_rows
offset = bandwidth + i - 1;
for j = 1:columns
%// Dynamic field name referencing
ROLLING_WINDOWS(i).(COMPANIES{j}) = RETURNS(i:offset, j);
end
end
end
Here's a great article by Loren Shure from MathWorks if you want to learn more: http://blogs.mathworks.com/loren/2005/12/13/use-dynamic-field-references/ ... but basically, if you have a string and you want to use this string to create a field, you would do:
str = '...';
s.(str) = ...;
s is your structure and str is the string you want to name your field.
I am creating a Frame work at Unix . This frame Work calls multiple DML's at different different Stages according to the dynamic situations.
All DML's contain set of sql queries. Each query comes under a defined Lable (Say Lable 1 .... Lable 2.... etc)
I want to make this framework so good so that it can trigger the DML from the required LABEL only ....(In case of Failure and restart job)..
So for that i need to take the Unix variable($LABLE) inside the DML's.
I am not getting ...how to do this???
Please help !!!! if you ever try this.....
Sample DML -
.LOGON TDPS/admin_dxwx_p05,store123;
.LABEL FIRST
CREATE SET TABLE DXWX_P05_DLWORK_DB01.WorKTable_1 ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
Customer_Name VARCHAR(25) CHARACTER SET LATIN NOT CASESPECIFIC,
Age INTEGER,
Product_Name VARCHAR(25) CHARACTER SET LATIN NOT CASESPECIFIC,
Price INTEGER)
PRIMARY INDEX ( Customer_Name );
.IF ERRORCODE <> 0 THEN .GOTO ERRORFOUND
.LABEL SECOND
CREATE SET TABLE DXWX_P05_DLWORK_DB01.WorkTable_2 ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
Customer_Name VARCHAR(25) CHARACTER SET LATIN NOT CASESPECIFIC,
Age INTEGER
Product_Name VARCHAR(25) CHARACTER SET LATIN NOT CASESPECIFIC,
Price INTEGER)
PRIMARY INDEX ( Customer_Name );
.IF ERRORCODE <> 0 THEN .GOTO ERRORFOUND
.LABEL THIRD
Insert into DXWX_P05_DLWORK_DB01.WorKTable_1
Sel * from
DXWX_P05_DLWORK_DB01.Source_Table;
.IF ERRORCODE <> 0 THEN .GOTO ERRORFOUND
.LABEL FOUR
Insert into DXWX_P05_DLWORK_DB01.WorKTable_2
Sel * from
DXWX_P05_DLWORK_DB01.WorKTable_1;
.IF ERRORCODE <> 0 THEN .GOTO ERRORFOUND
.QUIT 0;
.label ERRORFOUND
.QUIT 8;
.LOGOFF;
EOF
I'm trying to make the inventory scan process a bit simple.
We scan the incoming shipment. The scanner sends a string to Excel something like 'XYZ123'.
What I'm trying to do is, instead of the user selecting the column manually, I'd like excel to send the scanned string to the respective column.
For example, let us say there are 3 possible columns 'PO', 'Item', 'SN'
PO will start with 'XYZ'
Item will start with 'ABC'
SN with start with 'LMN'
If the scanned value starts with 'ABC', it has to be sent to the 'Item' column.
Is this possible? I tried playing with the Search, Exact and few other formulas, nothing worked so far.
Thank you,
Sub test_inventory_sorting()
Dim v_arr(1 To 3) As String, v As Variant
v_arr(1) = "XYZ1234"
v_arr(2) = "LMN73456"
v_arr(3) = "ABCzxcv"
For Each v In v_arr
Debug.Print v; " goes to "; get_col(CStr(v))
Next v
End Sub
Function get_col(ByVal p_val As String) As String
If p_val Like "ABC*" Then
get_col = "SN"
ElseIf p_val Like "LMN*" Then
get_col = "Item"
ElseIf p_val Like "XYZ*" Then
get_col = "PO"
Else
get_col = "Invalid"
End If
End Function
I have a stored procedure that is generating a string that it will call EXECUTE() on. The string contains an UPDATE statement. However, the columns and values that it is executing are not known beforehand. These are coming into the stored procedure through an XML string. I then use XML queries to get the data out and into a temporary table.
This is not sanitizing the data.
DECLARE #TBL_FLD TABLE (
TBL VARCHAR(MAX),
COL VARCHAR(MAX),
VAL VARCHAR(MAX)
);
-- Fill #TBL_FLD via xml parsing (omitted for brevity)
DECLARE TBL_CURSOR CURSOR FOR
SELECT distinct (TBL) FROM #TBL_FLD;
OPEN TBL_CURSOR;
WHILE 1 = 1
BEGIN
FETCH NEXT FROM TBL_CURSOR INTO #TABLE_NAME
IF ( ##FETCH_STATUS <> 0 )
BREAK
SET #SETTING_STR = '';
SELECT #SETTING_STR = STUFF( ( SELECT ', ' + COL + ' = ''' + VAL + '''' FROM #TBL_FLD WHERE TBL = #TABLE_NAME FOR XML PATH('') ), 1, 2, '');
SET #SQL_QUERY += 'UPDATE ' + #TABLE_NAME + ' SET ' + #SETTING_STR + ' WHERE KEY = ' + CONVERT(VARCHAR(MAX), #KEY_VAL) + '; ';
END
CLOSE TBL_CURSOR
DEALLOCATE TBL_CURSOR
EXECUTE (#SQL_QUERY);
I trust the COL field of #TBL_FLD, but the VAL will have come from a user. Which leaves a massive security hole since I am just concatenating the data together. There has to be a better way.
Since there are an unknown number of columns, I can't easily create parameters for the statement so that the data is cleaned up. If worst comes to worst, I can do it, (see the answer to Dynamically Create Update SQL In Stored Procedure) but it will be uglier than I would like.
Is there a function, or method, to sanitize the data, before I blindly add it to the statement? Or is there a better way to do what I'm trying to do?
I think that just doubling single quotes with a REPLACE on VAL would fix any issues since an attacker would not be able to exit the "string scope" to execute arbitrary code :
SELECT #SETTING_STR = STUFF( ( SELECT ', ' + COL + ' = ''' + REPLACE(VAL, '''', '''''') + '''' FROM #TBL_FLD WHERE TBL = #TABLE_NAME FOR XML PATH('') ), 1, 2, '');
I don't remember a built-in T-SQL function that does the same thing
I've created a Drop Down List Box (DDLB) in my window (I'm using PowerBuilder 10.5). Once I would call my function, the DDLB would fill with all the different cities from my table. This is the code I've used:
FOR li_i=1 TO ii_br_red
ls_city = dw_city.GetItemString(li_i, 'city')
IF ddlb_city.FindItem(ls_city, 1) = -1 THEN
ddlb_city.AddItem(ls_city) END IF; NEXT
Next part of the code is in the ddlb "selectionchanged" event...
dw_city.SetFilter("city = '" + this.text + "'")
dw_city.Filter()
This works great, and after calling my function (via click on a command button) I'd get a list of all different cities in my table, ex.
Paris
London
New York
Washington
No town would be listed twice.
What I need to do now is add a country next to every city in my DDLB. So that after clicking my command button I would get this in my DDLB:
Paris (France)
London (GB)
New York (USA)
Washington (USA)
Any advice? Thanks in advance...
SECOND QUESTION, similar to this subject: I have an SQL code:
SELECT distinct name FROM table1;
This gives me 8 different names. What I want to do is fill another DDLB, ddlb_1 with these names, but this must occur on the open event of my program. This is what I've written in the open event of my program:
string ls_name
SELECT distinct name INTO :ls_name FROM tabel1;
ddlb_1.AddItem(ls_name)
But this only gives me the first name. I'm guessing I need some kind of count, but I just can't pull it off.
If you do not want to change the design of the program, and as you states that the country is in the same DW, you could hack the code a little to add the country to the ddlb (I suppose that the country is available on the same row of the dw):
String ls_country
FOR li_i=1 TO ii_br_red
ls_city = dw_city.GetItemString(li_i, 'city')
IF ddlb_city.FindItem(ls_city, 1) = -1 THEN
ls_country = dw_city.GetItemString(li_i, 'country')
ddlb_city.AddItem(ls_city + ' (' + ls_country + ')')
END IF
NEXT
A quick and dirty hack to get back the value in the event to filter the DW would be
int p
string ls_city
ls_city = this.text
p = pos(ls_city, '(')
if p > 0 then ls_city = left(ls_city, p - 2) //skip the "space + (country)" part
dw_city.SetFilter("city = '" + ls_city + "'")
dw_city.Filter()
But this kind of code is difficult to maintain and should be replaced by something else, as the processing of the city value is strongly coupled to its representation in the list.
A better solution would be a dropdowndatawindow, or (worse) an array of the cities names where the index of a city + country in the ddlb would correspond to the index of the bare city name suitable for filtering the DW
I think you should modify the "source" datawindow' select, and you should get the final result which you want, and you would only need to copy the datas from the datawindow to the ddlb. You should use distinct in the select something like this:
select distinct city + ' (' + country_code + ')' from cities_and_countries_table
of course you should replace the "city", "country_code" to the actual column name in your table as well the table-name. With this you will get every city only once and they will be already concatenated with the country code.
Br. Gábor
Does it really have to be DDLB? I would give the user a Single Line Edit for the city name and filter the DW as the user types.
To answer my own second question, this is how I've done it finally...
String ls_name
DECLARE xy CURSOR FOR
SELECT distinct name FROM table1;
OPEN xy;
FETCH xy INTO :ls_name;
do until sqlca.sqlcode <> 0
ddlb_1.AddItem(ls_name);
FETCH xyINTO :ls_name;
loop
CLOSE xy;
I'm new to PowerBuilder, But I just used that kind of scenario, however I used a DDW (Drop Down Data Window) Instead of a List Box On this case, you can display more than one column as soon as the DW gets the focus and you'd be able to dynamically populate the data. Give it a try. It worked for me, DW's are a pain in the neck when you're just starting (as in my case) but you can do a lot with it