SSIS For Loop Container using a date variable for tables - date

I am trying to automate an SSIS package as in the current format it is using the data from tables from the past three years.
The layout of the table names is TableName_YYYYMM for 2 of them and TableName_YYYYjan or feb etc for the other.
How would I set out the for loop with a date variable so that the package is getting the data from the tables from 3 years ago for each month until the current month?
I am fairly new at SSIS so I have been unable to find any solution yet as I am not 100% sure on how I would go about implementing it.

It's hard to say, for me at least, without seeing the details of what you're doing.
While you're building out dates, you're also not as you're only working with year and month so using a date data type probably isn't going to help.
Create 5 SSIS variables
Year - Int32 - 0
Month - Int32 - 0
YYYYMM - String - Evaluate as Expression (DT_WSTR, 4) #[User::Year] + RIGHT("0" + (DT_WSTR, 2) #[User::Month], 2)
YYYYMonth - String - Evaluate as Expression (DT_WSTR, 4) #[User::Year] + #[User::MonthAbr]
MonthAbr - String - Evaluate as Expression
Expression
(#[User::Month] == 1) ? "Jan" :
(#[User::Month] == 2) ? "Feb" :
(#[User::Month] == 3) ? "Mar" :
(#[User::Month] == 4) ? "Apr" :
(#[User::Month] == 5) ? "May" :
(#[User::Month] == 6) ? "Jun" :
(#[User::Month] == 7) ? "Jul" :
(#[User::Month] == 8) ? "Aug" :
(#[User::Month] == 9) ? "Sep" :
(#[User::Month] == 10) ? "Oct" :
(#[User::Month] == 11) ? "Nov" :
(#[User::Month] == 12) ? "Dec" : "UNK"
At this point, if I change the value of Year or Month, those three variable change so that simplifies our problem to "how do we change those two?"
If I have whole year boundaries to work with i.e. 2000 to 2021 Jan to Dec every year, I'd use something like this package. A nested set of For Loop Containers. Outer enumerates years from 2000 to 2021. The inner enumerates 1 to 12. The Sequence Container inside is where I do the actual work. In this case, I simply print the value of my user variables to show it works.
For Loop - Year
Configuring the variable #[User::Year]
For Loop - Month
Configuring the variable #[User::Month]
My Script Task is my much beloved (by me at least) Test for Echo
Add a Script Task, language is C#. Select the User scoped variables we defined above as ReadOnly variables and use the following in the guts of the script
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
namespace ST_EchoBack
{
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public void Main()
{
bool fireAgain = false;
string message = "{0}::{1} : {2}";
foreach (var item in Dts.Variables)
{
Dts.Events.FireInformation(0, "SCR Echo Back", string.Format(message, item.Namespace, item.Name, item.Value), string.Empty, 0, ref fireAgain);
}
Dts.TaskResult = (int)ScriptResults.Success;
}
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
}
}
F5
At this point, if you run what I've shown, you'll get Output like the following
SSIS package "C:\Users\bfellows\source\repos\SO_Trash\SO_Trash\SO_74444363.dtsx" starting.
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Month->1
Information: 0x0 at SCR Echo back, SCR Echo Back: User::MonthAbr->Jan
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Year->2000
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMM->200001
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMonth->2000Jan
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Month->2
Information: 0x0 at SCR Echo back, SCR Echo Back: User::MonthAbr->Feb
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Year->2000
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMM->200002
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMonth->2000Feb
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Month->3
Information: 0x0 at SCR Echo back, SCR Echo Back: User::MonthAbr->Mar
Information: 0x0 at SCR Echo back, SCR Echo Back: User::Year->2000
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMM->200003
Information: 0x0 at SCR Echo back, SCR Echo Back: User::YYYYMonth->2000Mar
I like this approach to building packages as it's small, testable and non-destructive. We can confirm out looping and all this nonsense is working before we start lifting and shifting data.
What if I need to work with a partial year?
Fantastic question. I would add a sixth variable to the mix. Call this one something like IsValid and type of boolean. The idea is that you'll add in logic to handle the partial boundary condition. Let's assume we want to load the year 2022 but only through October (10). After adjusting the EvalExpression in FLC Year Enum to 2023, we'll use the following expression
(#[User::Year] == 2022 && #[User::Month] > 10) ? False : True
If the year is 2022 and the month is greater than 10, then False. All other combinations will yield True. I felt that was more concise than other forms of the test. If you run it now, you'd see the value of IsValid is True until we get to the last two loops of our run. Perfect!
You could either user a Precedent Constraint or set the Disable property on the SEQC Do Work.
Precedence Constraint approach
Inside the Sequence Container between the first task (an empty Sequence Container is an excellent anchor as it is a logical construction and requires no configuration) and the second task and set the Evaluation operation as Expression and Constraint. Value is Success. Expression is #[User::IsValid]
Disable approach
The thought here is that we'll set the Disable property of SEQC Do Work via Expression. I find this a little confusing from a maintenance perspective as we need to work with the inverse of IsValid because we only want to Disable when it's not valid.
Right click on SEQC Do Work
Properties
Expressions
Select Disable -> !#[User::IsValid]
Ok
Ok
Do I need to create all these variables
Yes. You might even want some more. Anything that has an expression on it cannot be debugged except through observation of the computed result. I can't put a break point between (DT_WSTR, 4) #[User::Year] and + RIGHT("0" + (DT_WSTR, 2) #[User::Month], 2) to see whether I'm building out a 2 character month "number." The only thing I can do is look at the value of a variable. Which means, do not use anything in a Task or Containers expression that is not a single Variable. I do violate it above with Disable and that's why I also note it as a maintenance overhead/technical debt.
If I get any weird behaviour while I'm testing this, I can set a breakpoint immediately before the weird task/container and inspect my variables and trace back to why it's weird. If I had the same expression from YYYYMonth but all in a single go attached to an OLE DB Source, good luck trying to figure out why it's pulling incorrect data.
How can I make the boundaries dynamic?
Add a pair of Variables
YearInitial - Int32 - 2000
YearTerminal - Int32 - Evaluate as expression YEAR(#[System::StartTime])

Related

Am trying to read a population_data.json file but when ever i run the code it doesn't display any data in the file and i don't get any Error too?

import json
#Load the data into a list.
filename = 'population_data.json'
with open(filename)as f:`enter code here`
pop_data = json.load(f)
enter code here
#Print the 2010 population data for each country.
for pop_dict in pop_data:`enter code here`
if pop_dict['Year'] == '2010':
country_name = pop_dict['Country Name']
population = int(float(pop_dict['Value']))
print(country_name + " : " + str(population))
Am trying to extract data from a population_data.json file, but whenever i run my code it doesn't show any result and i don't get any Errors, i have save the population data file in the same folder with the code but i still have that same problem, i don't get any result of the data in the shell. i would be glad if someone can help.Thank you .
enter code here
import json
#Load the data into a list.
filename = 'population_data.json'
with open(filename)as f:`enter code here`
pop_data = json.load(f)
enter code here
#Print the 2010 population data for each country.
for pop_dict in pop_data:`enter code here`
if pop_dict['Year'] == '2010':
country_name = pop_dict['Country Name']
population = int(float(pop_dict['Value']))
print(country_name + " : " + str(population))
I would suggest starting your script in the python debugger (pdb). You can start it either by starting your script like this:
python3 -m pdb your_script.py
or by importing the pdb module. Adding the following line into your python file (for example after import json, or any other):
import pdb; pdb.set_trace()
Once it is loaded, the debugger stops at the first instruction and shows the debugger promt (Pdb) . Continue execution line by line by typing next and hitting ENTER until you hit a line where something interesting is happening. The debugger prints always the next instruction and the script path and line.
Debugger output is added below, everything after (Pdb) you have to type and confirm with ENTER
(Pdb) next
> /path/to/your_script.py(7)<module>()
-> pop_data = json.load(f)
(Pdb) next
> /path/to/your_script.py(11)<module>()
-> for pop_dict in pop_data:
Now let the debugger print contents of a variable
(Pdb) p pop_data
{"Probably": "something", "you": "don't expect here"}
I suspect either the for loop yields 0 pop_dicts and therefore the loop body is never executed, or no pop_dict key Year has value 2010 and the if body is never executed.
Alternative to type next often (== single stepping): set a break point on a specific line (your_script.py:11), and continue execution until the breakpoint is hit
(Pdb) break your_script.py:11
Breakpoint 1 at /path/to/your_script.py:11
(Pdb) continue
> /path/to/your_script.py(11)<module>()
-> for pop_dict in pop_data:
(Pdb)
For additional debugger commands see pdb commands

How to pass a mixture of strings and arrays to an executable from PowerShell?

I'm trying to call vswhere.exe to find various Visual Studio executables for CI purposes. In order to simplify this, I've created a wrapper function:
function Run-VsWhere { &("${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe") $args }
function Find-VSWherePath([string[]] $workloads, [string] $pathGlob) {
Run-VsWhere -products * -prerelease -latest -requires $workloads -requiresAny -find $pathGlob
}
This works perfectly for single workloads, e.g. for MSBuild:
Find-VSWherePath "Microsoft.Component.MSBuild" "MSBuild/**/Bin/MSBuild.exe"
> C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Current\Bin\MSBuild.exe
... but falls apart for multiple ones like VSTest:
Find-VSWherePath "Microsoft.VisualStudio.Workload.ManagedDesktop","Microsoft.VisualStudio.Workload.Web" "**/TestPlatform/vstest.console.exe"
> (nothing)
If I replace the call to vswhere with a call to echoargs, it demonstrates exactly what's going wrong. MSBuild:
> Arg 0 is <-products>
> Arg 1 is <*>
> Arg 2 is <-prerelease>
> Arg 3 is <-latest>
> Arg 4 is <-requires>
> Arg 5 is <Microsoft.Component.MSBuild>
> Arg 6 is <-requiresAny>
> Arg 7 is <-find>
> Arg 8 is <MSBuild/**/Bin/MSBuild.exe>
vs VSTest:
> Arg 0 is <-products>
> Arg 1 is <*>
> Arg 2 is <-prerelease>
> Arg 3 is <-latest>
> Arg 4 is <-requires>
> Arg 5 is <Microsoft.VisualStudio.Workload.ManagedDesktop Microsoft.VisualStudio.Workload.Web>
> Arg 6 is <-requiresAny>
> Arg 7 is <-find>
> Arg 8 is <**/TestPlatform/vstest.console.exe>
The issue is that the $workloads parameter is being passed to Run-VsWhere as a single parameter joined by a space, instead of one parameter per element in the array - how can I force it to pass as I need? I've tried every combination of splatting, splitting, joining, single-quoting, double-quoting... but nothing seems to work.
Using the automatic variable $args passes the arguments as they're provided, meaning that an array argument nested in $args is passed as-is (i.e. remains an array). Use splatting (#args) to flatten/unroll nested arrays.
function Run-VsWhere {
& "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" #args
}
Is it possible your second call to Find-VSWherePath does not result in anything because the specified workloads are not available? I tried the code below and it does work.
function Find-VSWherePath([string[]] $workloads, [string] $pathGlob) {
. "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" -products * -prerelease -latest -requires $workloads -requiresAny -find $pathGlob
}
clear
"This works"
Find-VSWherePath "Microsoft.Component.MSBuild" "MSBuild/**/Bin/MSBuild.exe"
"No result"
Find-VSWherePath "Microsoft.VisualStudio.Workload.Web" "MSBuild/**/Bin/MSBuild.exe"
"Try two workloads, the first is not available, but the second is. This also works."
Find-VSWherePath "Microsoft.VisualStudio.Workload.Web","Microsoft.VisualStudio.Component.NuGet" "MSBuild/**/Bin/MSBuild.exe"

How to get month as integer in Visual Basic Script?

I'm trying to get the current month as a short string in a Visual Basic Script:
Dim month
Dim mth
month = Now.Month ' This doesn't work.
month = Now().Month ' Tried this too.
month = Month(Now) ' Also tried this.
mth = MonthName(month, True) ' (e.g. "Apr" or "Mar")
However, I keep getting runtime errors:
Microsoft VBScript runtime error: Object required: 'Now'
Microsoft VBScript runtime error: Object required: 'Now()'
Microsoft VBScript runtime error: Type mismatch 'Month'
I'm able to use Now fine as a string:
CStr(Now)
Or as a plain value
Dim val
val = Now
How can I use Now as an Object for accessing its member functions? Perhaps I am confusing Visual Basic functionality with VB Script?
VBScript Dates are not objects - use the Month(SomeDate) function to get the month number of SomeDate:
>> WScript.Echo Month(Now)
>>
4
To get the month's name (abreviated or full), use:
>> WScript.Echo MonthName(Month(Now()),False)
>> WScript.Echo MonthName(Month(Now()),True)
>>
April
Apr
(stolen from #collapsar, but using the correct (boolean) type of the second parameter).
Official Docs for
Month(): here
MonthName(): here
[The samples given are 'living' code, if you get errors, your code is to blame]
try this line of code:
MonthName(Month(Now()),1)
a standalone solution:
Dim imonth
Dim mth
imonth = Month(Now()) ' Also tried this.
mth = MonthName(Month(Now()))
WScript.Echo "mth = " & mth
WScript.Echo "mth_short = )" & MonthName(Month(Now()),1)
The issue is that Visual Basic Script is not case sensitive and my variable month was colliding with the built in Month.
As given here and here
Syntax:
Month(Now)

Use of diff command from tcl script captures error 'child process exited abnormally'

In tcl script I am using diff command to compare the files line by line
if {[catch {eval exec "diff /tmp/tECSv2_P_HTTP_XHDR_URL_FUNC_12.itcl /tmp/tempReformat"} results]} {
puts "$results"
}
Output of diff command is obtained properly but it catches error 'child process exited abnormally'
Output:
==>tclsh diffUsingScript
992c992
< fail "Redirection is not reflected in css messages"
---
> fail "Redirection is not reflected in css messages"
child process exited abnormally
So whats going wrong due to which this error is obtained. I want diff operation to be error free in my tcl script
From my diff(1): "Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."
Since non-zero returns are the usual way to report errors in shell scripts, tcl and diff disagree on the meaning of the return result. It's probably really convenient for writing shell scripts to know whether or not two files are different from the return value, but I don't see any mechanism to disable that from the manpage. (I'd rather use cmp -q for just getting whether or not two files are different, not sure why the diff people made the decision they did.)
But you can bludgeon this into working by appending ; true to your command.
A more artful way to make it work would be to error only on an exit code of 2: diff foo bar ; if [ $? -ne 2 ]; then true ; else false; fi;
Check the results with different filenames and echo $? after each test to see which ones are returning 0 (from true) and which ones are returning 1 (from false).
The way to handle this in Tcl is:
set rc [catch {exec diff f1 f2} output]
if {$rc == 0} {
puts "no difference"
} else {
if {[lindex $::errorCode 0] eq "CHILDSTATUS"} {
if {[lindex $::errorCode 2] == 1} {
puts "difference"
# show output without "child process exited abnormally" message
puts [string replace $output end-31 end ""]
} else {
puts "diff error: $output"
}
} else {
puts "error calling diff: $output"
}
}
See the discussion on the Tcl Wiki exec page.

Trouble using 'while' loop to evaluate multiple lines, Perl

Thank you in advance for indulging an amateur Perl question. I'm extracting some data from a large, unformatted text file, and am having trouble combining the use of a 'while' loop and regular expression matching over multiple lines.
First, a sample of the data:
01-034575 18/12/2007 258,750.00 11,559.00 36 -2 0 6 -3 2 -2 0 2 1 -1 3 0 5 15
-13 -44 -74 -104 -134 -165 -196 -226 -257 -287 -318 -349 -377 -408 -438
-469 -510 -541 -572 -602 -633 -663
Atraso Promedio ---> 0.94
The first sequence, XX-XXXXXX is a loan ID number. The date and the following two numbers aren't important. '36' is the number of payments. The following sequence of positive and negative numbers represent how late/early this client was for this loan at each of the 36 payment periods. The '0.94' following 'Atraso Promedio' is the bank's calculation for average delay. The problem is it's wrong, since they substitute all negative (i.e. early) payments in the series with zeros, effectively over-stating how risky a client is. I need to write a program that extracts ID and number of payments, and then dynamically calculates a multi-line average delay.
Here's what I have so far:
#Create an output file
open(OUT, ">out.csv");
print OUT "Loan_ID,Atraso_promedio,Atraso_alt,N_payments,\n";
open(MYINPUTFILE, "<DATA.txt");
while(<MYINPUTFILE>){
chomp($_);
if($ID_select != 1 && m/(\d{2}\-\d{6})/){$Loan_ID = $1, $ID_select = 1}
if($ID_select == 1 && m/\d{1,2},\d{1,3}\.00\s+\d{1,2},\d{1,3}\.00\s+(\d{1,2})/) {$N_payments = $1, $Payment_find = 1};
if($Payment_find == 1 && $ID_select == 1){
while(m/\s{2,}(\-?\d{1,3})/g){
$N++;
$SUM = $SUM + $1;
print OUT "$Loan_ID,$1\n"; #THIS SHOWS ME WHAT NUMBERS THE CODE IS GRABBING. ACTUAL OUTPUT WILL BE WRITTEN BELOW
print $Loan_ID,"\n";
}
if(m/---> *(\d*.\d*)/){$Atraso = $1, $Atraso_select = 1}
if($ID_select == 1 && $Payment_find == 1 && $Atraso_select == 1){
...
There's more, but the while loop is where the program is breaking down. The problem is with the pattern modifier, 'g,' which performs a global search of the string. This makes the program grab numbers that I don't want, such as the '1' in loan ID and the '36' for the number of payments. I need the while loop to start from wherever the previous line in the code left off, which should be right after it has identified the number of loans. I've tried every pattern modifier that I've been able to look up, and only 'g' keeps me out of an infinite loop. I need the while loop to go to the end of the line, then start on the next one without combing over the parts of the string already fed through the program.
Thoughts? Does this make sense? Would be immensely grateful for any help you can offer. This work is pro-bono, unpaid: just trying to help out some friends in a micro-lending institution conduct a risk analysis.
Cheers,
Aaron
The problem is probably easier using split, for instance something like this:
use strict;
use warnings;
open DATA, "<DATA.txt" or die "$!";
my #payments;
my $numberOfPayments;
my $loanNumber;
while(<DATA>)
{
if(/\b\d{2}-\d{6}\b/)
{
($loanNumber, undef, undef, undef, $numberOfPayments, #payments) = split;
}
elsif(/Atraso Promedio/)
{
my (undef, undef, undef, $atrasoPromedio) = split;
# Calculate average of payments and print results
}
else
{
push(#payments, split);
}
}
If the data's clean enough, I might approach it by using split instead of regular expressions. The first line is identifiable if field[0] matches the form of a loan number and field[1] matches the format of a date; then the payment dates are an array slice of field[5..-1]. Similarly testing the first field of each line tells you where you are in the data.
Peter van her Heijden's answer is a nice simplification for a solution.
To answer the OP's question about getting the regexp to continue where it left off, see Perl operators - regexp-quote-like operators, specifically the section "Matching in list context" and the "\G assertion" section just after that.
Essentially, you can use m//gc along with the \G assertion to use regexps match where previous matches left off.
The example in the "\G assertion" section about lex-like scanners would seem to apply to this question.