How to limit the data entered within a stored procedure? - stored-procedures

I have a stored procedure that prompts user for input and stores/updates my table. My issue is that when trying to add limitations when entering number it does not work as planned.
Here is the table:
Drop table incident;
CREATE TABLE INCIDENT
(ICD_ID VARCHAR2 (8) NOT NULL PRIMARY KEY,
PLR_ID VARCHAR2 (11),
M_ID VARCHAR2 (10),
PP_ID VARCHAR2 (10),
I_POINTS NUMBER (8));
INSERT INTO INCIDENT VALUES
('ICD01', 'CHE01', 'M01', 'PP01', NULL);
INSERT INTO INCIDENT VALUES
('ICD02', 'CHE03', 'M07', 'PP02', NULL);
INSERT INTO INCIDENT VALUES
('ICD03', 'CHE03', 'M04', 'PP03', NULL);
INSERT INTO INCIDENT VALUES
('ICD04', 'KLN04', 'M07', 'PP02', NULL);
INSERT INTO INCIDENT VALUES
('ICD05', 'CHE01', 'M04', 'PP03', NULL);
When prompted, I enter the ICD_ID (for example 'ICD03') *must be capital* entry.
The procedure:
DECLARE
v_icd_id incident.icd_id%type := &INCIDENT_ID;
v_inc_I_POINTS incident.i_points%type := &PENALTY_POINTS;
v_I_POINTS incident.I_POINTS%type;
V_PP_ID INCIDENT.PP_ID%TYPE;
E_TOO_MANY_POINTS exception;
E_UPDATE_HIGH exception;
E_A exception;
E_B exception;
E_C exception;
BEGIN
SELECT I_POINTS INTO v_I_POINTS
FROM incident WHERE icd_id = v_icd_id;
IF v_I_POINTS > 10 THEN
RAISE E_UPDATE_HIGH;
END IF;
IF v_inc_I_POINTS >10 THEN
RAISE E_TOO_MANY_POINTS;
END IF;
IF V_PP_ID = 'PP03' OR V_INC_I_POINTS > 10 OR V_INC_I_POINTS < 10 THEN
RAISE E_A;
END IF;
UPDATE incident SET i_points = (v_inc_i_points + NVL(i_points,0))
WHERE icd_id=v_icd_id;
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('=====================');
DBMS_OUTPUT.PUT_LINE('PENALTY POINTS UPDATED!');
DBMS_OUTPUT.PUT_LINE('=====================');
DBMS_OUTPUT.PUT_LINE('');
EXCEPTION
WHEN E_TOO_MANY_POINTS THEN
DBMS_OUTPUT.PUT_LINE('==========================================================');
DBMS_OUTPUT.PUT_LINE('TOO MANY POINTS ALLOCATED THERE! ACTION ABORTED.');
DBMS_OUTPUT.PUT_LINE('==========================================================');
WHEN E_UPDATE_HIGH THEN
DBMS_OUTPUT.PUT_LINE('=============================================================');
DBMS_OUTPUT.PUT_LINE('CAN NOT STORE THAT MANY PENALTY POINTS THERE! ACTION ABORTED.');
DBMS_OUTPUT.PUT_LINE('=============================================================');
WHEN E_A THEN
DBMS_OUTPUT.PUT_LINE('=============================================================');
DBMS_OUTPUT.PUT_LINE('CAN ONLY ALLOCATED 10 POINTS HERE! ACTION ABORTED.');
DBMS_OUTPUT.PUT_LINE('=============================================================');
WHEN E_B THEN
DBMS_OUTPUT.PUT_LINE('=============================================================');
DBMS_OUTPUT.PUT_LINE('CAN ONLY ALLOCATED 5 POINTS HERE! ACTION ABORTED.');
DBMS_OUTPUT.PUT_LINE('=============================================================');
WHEN E_C THEN
DBMS_OUTPUT.PUT_LINE('=============================================================');
DBMS_OUTPUT.PUT_LINE('CAN ONLY ALLOCATED 1 POINTS HERE! ACTION ABORTED.');
DBMS_OUTPUT.PUT_LINE('=============================================================');
END;
/
What I am trying to accomplish:
Is to prevent people entering values of more than 10 and less than 10 where the PP_ID is "PP03".
For this I applied:
IF V_PP_ID = 'PP03' OR V_INC_I_POINTS > 10 OR V_INC_I_POINTS < 10 THEN
RAISE E_A;
END IF;
That works fine, but when I add another limitation like:
To prevent people entering values of more than 5 and less than 5 where the PP_ID is "PP02".
IF V_PP_ID = 'PP02' OR V_INC_I_POINTS > 5 OR V_INC_I_POINTS < 5 THEN
RAISE E_B;
END IF;
OR
To prevent people entering values of more than 1 and less than 1 where the PP_ID is "PP01".
IF V_PP_ID = 'PP01' OR V_INC_I_POINTS > 1 OR V_INC_I_POINTS < 1 THEN
RAISE E_C;
END IF;
It does not work and only follows the first one. How can I overcome this? Thanks in advance. Help is greatly appreciated.

I think your condition is incorrect:
IF V_PP_ID = 'PP03' OR V_INC_I_POINTS > 10 OR V_INC_I_POINTS < 10 THEN will evaluate to true whenever V_PP_ID equals 'PP03' (regardless of the value of V_INC_I_POINTS) AND whenever V_INC_I_POINTS is not equal to 10 (regardless of the value of V_PP_ID).
I believe the intended coniditon is:
IF V_PP_ID = 'PP03' AND (V_INC_I_POINTS > 10 OR V_INC_I_POINTS < 10) THEN
or simply:
IF V_PP_ID = 'PP03' AND V_INC_I_POINTS != 10 THEN
(The rest of your conditions regarding 'PP02' and 'PP01' should be modified accordingly.)
EDIT:
I missed it the first time, but it seems that "V_PP_ID" is never set to any value. We need to instanciate it to the value of the selected row's PP_ID column.
Try replacing:
SELECT I_POINTS INTO v_I_POINTS
FROM incident WHERE icd_id = v_icd_id;
with
SELECT I_POINTS, PP_ID INTO v_I_POINTS, V_PP_ID
FROM incident WHERE icd_id = v_icd_id;

Related

Postgres 9.5.4 Window in update trigger function (lag and lead)

Been banging away and would like some help
After running a lot of mathematical functions on rails and finding spurious results I decided to climb down to do it in the DB to find it's all very do-able for me however as soon as I get to the second function I have the following errors:
PG::UndefinedFunction: ERROR: function updown(integer) does not exist
LINE 3: SELECT "updown" (lag(updown) over (order by id)) table_nam...
^ HINT: No function matches the given name and argument types.
Migration:
class CreateSmoothings < ActiveRecord::Migration
def change
create_table :smoothings do |t|
t.decimal :firstsmprice, :precision => 8, :scale => 6
t.decimal :finalsmprice, :precision => 8, :scale => 6
t.decimal :firstsmdelta, :precision => 8, :scale => 6
t.decimal :finalsmdelta, :precision => 8, :scale => 6
t.integer :updown, :null => false, :default => 0
t.integer :posture
t.integer :step
t.references :usdzar, index: true, foreign_key: true
t.references :smoothval, index: true, foreign_key: true
t.timestamps null: false
end
add_index :smoothings, :finalsmprice
end
end
I have an update controller that sets the data in smoothings from another table and this is when the trigger is fired. The first trigger works like a charm. This is the controller action and then the trigger and function.
# POST /smoothings works and does the insert of all data to the smoothings table
def create
# Do the 1st and 2nd SMOOTHINGS
# Get the row with the smoothval_id parameter to use these
sv = Smoothval.find(smoothing_params[:smoothval_id])
fval = sv.fval
sval = sv.sval
svid = sv.id
toremove = Smoothing.where(:smoothval_id => sv.id)
toremove.destroy_all
# For every entry in the table do:
collection = Usdzar.all
collection.each_with_index do |usdzar, i|
# Declare the previous variables required for id = 1 as
# the calculation cannot be done as it has no previous id
# I am sure this can be refactored
if usdzar.id == 1
prevprice = usdzar.price
prevfirstsmprice = usdzar.price
prevfinalsmprice = usdzar.price
else
prevprice = collection[i-1].price
prevfirstsmprice = Smoothing.last.firstsmprice
prevfinalsmprice = Smoothing.last.finalsmprice
end
# Do the smoothing calcs for the first smoothing using fval (first value)
firstsmprice = (((fval * usdzar.delta)/100) + prevprice)
# Find the difference of the current and previous of the first smoothing
firstsmdelta = firstsmprice - prevfirstsmprice
# Do the final smooting with the fval
finalsmprice = ((sval * firstsmdelta)/100) + prevfirstsmprice
# Find the differance of the current and previous of the second smoothing
finalsmdelta = finalsmprice - prevfinalsmprice
# Create the rows at end of iteration
Smoothing.create!(
usdzar_id: usdzar.id,
smoothval_id: sv.id,
firstsmprice: firstsmprice,
firstsmdelta: firstsmdelta,
finalsmprice: finalsmprice,
finalsmdelta: finalsmdelta
)
end
redirect_to smoothings_url, notice: 'Old smoothings removed and new smoothings successfully created.'
The trigger function and trigger that works :-)
CREATE TRIGGER a_smoothings_trigger
AFTER INSERT
ON smoothings
FOR EACH ROW
EXECUTE PROCEDURE make_updowns_trg();
CREATE FUNCTION make_updowns_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
update smoothings
set updown = case when (finalsmdelta < 0) then 1
when (finalsmdelta > 0 ) then 0
else 2 end;
RETURN NULL;
END
$BODY$;
This (the above) runs well.
What I am trying to do next is extend the function by another trigger and function (knowing now that they run in alphabetical order
The trigger/function is meant to take the "updown" (being 0, 1 or 2: up, down or same is the enum in rails) and:
If updown and next updown are the same then update as 2 (in between)
If the updown is greater than next "updown" update as 1 (high)
If the updown is less then next "updown" update as 0 (low)
CREATE FUNCTION public.make_high_lows_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
WITH next_updown AS
(SELECT updown (lag(updown) over (order by id)) table_name FROM smoothings
ORDER BY id DESC)
update smoothings
set posture = case when updown < next_updown then 1
when updown > next_updown then 0
else 2 end;
RETURN NULL;
END
$BODY$;
CREATE TRIGGER b_smoothings_trigger
AFTER INSERT
ON public.smoothings
FOR EACH ROW
EXECUTE PROCEDURE public.make_high_lows_trg();
Model for the controller:
class Smoothing < ActiveRecord::Base
enum updown: [:up, :down, :same]
enum posture: [:high, :low, :inbetween]
belongs_to :usdzar
belongs_to :smoothval
end
I had this all running in the controller as my model method coding really sucks and got it all running however after testing found the data to be very unreliable. Thanks in advance for the help. I am really astounded by the number of functions in Postgres and when I came across the window function thought it would be appropriate but can get over the hurdle. I know that (from the psql docs) you cannot put the lag/lead windowing into the update part of the function but have an issue obviously with how to do the sql block correctly
This query runs after running the first but how do I update the table on this:
with myUpdowns as (
Select id, updown, Lag(updown) over (order by id desc) next_updown from smoothings
)
select *
from myUpdowns
where updown < next_updown;
AND now I got one high to update correctly: I am now trying to do the full spread of high. low and inbetween (0, 1 and 2). If you have any suggestions I would love to hear them. The updated trigger that adds the high to the table looks as follows:
CREATE FUNCTION make_high_lows_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
UPDATE smoothings
SET posture = 0
FROM (
Select id, updown, Lag(updown) over (order by id desc) next_updown from smoothings
) as highs
WHERE highs.updown < highs.next_updown
AND smoothings.id = highs.id;
RETURN NULL;
END
$BODY$;
The Trigger:
CREATE TRIGGER b_smoothings_trigger
AFTER INSERT
ON smoothings
FOR EACH ROW
EXECUTE PROCEDURE make_high_lows_trg();
Hmm, now how to do low and inbetween, actually just the high and low are good

Create index for boolean columns

I have table that has 5 boolean columns.
on_stock | paid | received_payment | on_the_way | received
How to create an index for this table? Should I do it? I want to optimize a query like this:
SELECT "order".* FROM "order" INNER JOIN "seller_users" ON "order"."seller_foreign_id" = "seller_users"."google_id"
WHERE(((("order"."on_stock" <> true AND "order"."on_the_way" <> true ) AND "order"."paid" <> true ) AND "order"."received_payment" <> true ) AND "order"."received" <> true ) AND ("seller_users"."google_id" = 'lala#gmail.com' )
ORDER BY "order"."updated_at" DESC ;
When I try add this index - nothing happens. This index is not used.
add_index :order, [:on_stock, :on_the_way, :paid, :received_payment, :received], :name => "state_index"
If I add separate index for each columns - nothing happens too.
EXPLAIN ANALYZE output:
http://explain.depesz.com/s/FS2
Your table has a total of 8 rows, an index is not needed in this case. It is way faster to test each of this 8 rows against the where clause than to use an index here.

ROR + Count of all JSON value present in one column

In my rails app, I want to add few values present in one column in the form of key-value pair. I am not getting the way to add them.
(byebug) p #timing_params.data_date_wise
{"2"=>"7", "3"=>"8", "4"=>"9", "5"=>"10", "6"=>"11", "9"=>"", "10"=>"", "11"=>""
, "12"=>"", "13"=>"", "16"=>"", "17"=>"", "18"=>"", "19"=>"", "20"=>"", "23"=>""
, "24"=>"", "25"=>"", "26"=>"", "27"=>"", "30"=>"", "31"=>""}
Controller:
total_hour = 0
total_day_count = #timing_params.data_date_wise.count
puts "total_day_count = #{total_day_count}"
for i in 1..total_day_count
total_hour+= #timing_params.data_date_wise["i"] if #timing_params.data_date_wise["i"].to_i > 0
puts "date : #{#timing_params.data_date_wise['i']}"
end
puts "TotalHour : #{total_hour}"
Another problem I think is - All details are not in sequence so that only values will be calculated. For example count is 22 then as per the data - 23 to 31 will be missed.
Please suggest something...
How about changing your for loop to iterate through the hash like this:
#timing_params.data_date_wise.each do |date,value|
total_hour += value.to_i if value.to_i > 0
end

How do I pass my variable value into my stored procedure

I created a variable className and I assigned values to it.
I have another procedure in oracle that sends emails to me.
How do I pass this value into header and body of my email?
VARIABLE className varchar2(30)
:classname := 0;
BEGIN
FOR i IN
(
SELECT CLASS_INSTANCE_COUNT , CLASS_NAME
FROM MODEL_CLASS_COUNTS
WHERE TRUNC(COUNT_DATETIME) = TRUNC(SYSDATE)
)
LOOP
IF i.CLASS_INSTANCE_COUNT = 0
THEN
:className := i.CLASS_NAME;
EMAIL('myemail#col.com', 'email header: &className is 0', 'body: count for &className is 0');
END IF;
END LOOP;
END;
/
My guess is that you don't want to have either a SQL*Plus variable or a substitution variable. I'm guessing that you just want
BEGIN
FOR i IN
(
SELECT CLASS_INSTANCE_COUNT , CLASS_NAME
FROM MODEL_CLASS_COUNTS
WHERE TRUNC(COUNT_DATETIME) = TRUNC(SYSDATE)
)
LOOP
IF i.CLASS_INSTANCE_COUNT = 0
THEN
EMAIL('myemail#col.com',
'email header: ' || i.class_name || ' is 0',
'body: count for ' || i.class_name || ' is 0');
END IF;
END LOOP;
END;

Parsing EMail with VB6 - not splitting properly

When I parse through an email that looks like:
included_po~301993959 'VBCRLF
po_no~vendor~part_no~class~type_code~buyer~qty_ordered~measure~balance_on_order~cost~amt_ordered~order_date~delivery_date~ship_Date~receive_date~open~wo_no 'VBCRLF
301993959~100000~88008K~PROBE 800~F~VAX~4.0 ~EA~4.0~100.3300~401.32000~011513~012313~012313~000000~Y~STOCK 'VBCRLF
301993959~100000~TFCI-010-50~WIRE SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~STOCK 'VBCRLF
301993959~100000~TFIR-010-50~WIRE SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~STOCK 'VBCRLF
using code:
Public Sub AddItems()
Dim aLineItem() As String
Dim aItem() As String
Dim i As Integer
Dim j As Integer
Dim iCnt As Double
Dim msg
Dim Item As Items
ReDim sWo(0)
iCnt = 0
For i = 1 To UBound(sMsg())
aLineItem = Split(sMsg(i), vbCrLf)
For j = 1 To UBound(aLineItem)
If aLineItem(j) <> "" Then
If blah = 1 Then
Debug.Print ("...." & aLineItem(j))
End If
aItem = Split(aLineItem(j), "~")
If (aItem(0) <> "") And (aItem(0) <> "included_po") And (aItem(0) <> "po_no") Then
Item.PO_num = GetWo(aItem(1))
If Item.PO_num <> "0" Then
Item.Company = aItem(1)
Item.Delivery_date = aItem(12)
Item.pn = aItem(2)
Item.QTY_ordered = aItem(6)
Item.Unit_Price = aItem(9)
End If 'If Item.PO_num <> "0" Then
End If 'If aItem(1) <> "" Then
End If ' If aLineItem(j) <> "" Then
Next j
Next i
End Sub
The email parsing looks like this:
....301993959~100000~88008K~PROBE
....8800~F~VAX~4.0~EA~4.0~100.3300~401.32000~011513~012313~012313~000000~Y~S
....TOCK
Is there a better way to parse this email?
EDIT #1:
Public Sub GetMailMsg()
Dim pop3 As jmail.pop3
Dim iCount As Integer
Dim i As Integer
Dim mailID As Integer
Dim j As Integer
Dim sSubject As String
j = 0
ReDim sMsg(0)
'connect to the mail box
Set pop3 = New pop3
pop3.Connect "REMOVED FOR SECURITY"
'Get message count
iCount = pop3.Count
'Read Messages
For i = 1 To iCount
sSubject = pop3.Messages.Item(i).Subject
Label1.Caption = "Reading message.." & sSubject
DoEvents
If InStr(sSubject, "China Purchase Orders") <> 0 Then 'email test
j = j + 1
ReDim Preserve sMsg(j)
sMsg(j) = pop3.Messages.Item(i).Body
Label1.Caption = "Reading mail message for order " & sSubject
If blah = 1 Then
Debug.Print ("Reading mail message for order " & sSubject)
Debug.Print ("Reading mail message for order " & sMsg(j))
End If
End If
Next
pop3.Disconnect
Set pop3 = Nothing
End SUb
--EDIT #2
OUTPUT FROM DEBUG:
Reading mail message for order China Purchase Orders
Reading mail message for order
included_po~301993959
po_no~vendor~part_no~class~type_code~buyer~qty_ordered~measure~balance_o 'VBCRLF
n_order~cost~amt_ordered~order_date~delivery_date~ship_Date~receive_date 'VBCRLF
~open~wo_no 'VBCRLF
301993959~100000~88008K~PROBE 'VBCRLF
8800~F~VAX~4.0~EA~4.0~100.3300~401.32000~011513~012313~012313~000000~Y~S 'VBCRLF
TOCK 'VBCRLF
301993959~100000~TFCI-010-50~WIRE 'VBCRLF
SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~ST 'VBCRLF
OCK 'VBCRLF
301993959~100000~TFIR-010-50~WIRE 'VBCRLF
SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~ST 'VBCRLF
OCK 'VBCRLF
Your lines aren't breaking up properly makes me think there is a problem in the line aLineItem = Split(sMsg(i), vbCrLf). Please check whether you don't have any vbCrLF in the line you aren't getting right.
Is that line one continuous line?
Edit #1 # Jan 16, 2013 1:16pm EST:
I created a file and pasted provided input.
I created a simple program and incorporated your code into it. Please see produced DEBUG below, consisting as expected with 3 lines beginning with 301993959 and ending with STOCK. This is the correct and desirable output, right?
....301993959~100000~TFCI-010-50~WIRE SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~STOCK
....301993959~100000~TFIR-010-50~WIRE SPTEF~F~VAX~1.0~SP~1.0~12.6400~12.64000~011513~012313~012313~000000~Y~STOCK
....301993959~100000~88008K~PROBE 800~F~VAX~4.0 ~EA~4.0~100.3300~401.32000~011513~012313~012313~000000~Y~STOCK
So the code you provided is working fine. If you say the input file is fine too, that means the issue is with how you are reading that email, specifically how you populate sMsg(). Can you post that code please?
Edit #2 # Jan 16, 2013 2:32pm EST:
You are getting some unwanted vbCrLfs in your single record line and you can't split it the way you want. Here is what I suggest you do:
1) If you can modify GetMailMsg to instead of loading Body as it is, go through line by line to avoid generating unwanted vbCrLfs. I have zero experience with pop objects so I don't know if that is possible.
2) If the number of unwanted cbCrLfs is consistent, ex: 2 in a single record, then you can simply adjust your code to concatenate split records like this
DIM concatLine As String
For j = 1 To UBound(aLineItem) Step 3
concatLine = aLineItem(j) & aLineItem(j+1) & aLineItem(j+2)
If concatLine <> "" Then
If blah = 1 Then
Debug.Print ("...." & concatLine)
End If
aItem = Split(concatLine, "~")
If (aItem(0) <> "") And (aItem(0) <> "included_po") And (aItem(0) <> "po_no") Then
Item.PO_num = GetWo(aItem(1))
If Item.PO_num <> "0" Then
Item.Company = aItem(1)
Item.Delivery_date = aItem(12)
Item.pn = aItem(2)
Item.QTY_ordered = aItem(6)
Item.Unit_Price = aItem(9)
End If 'If Item.PO_num <> "0" Then
End If 'If aItem(1) <> "" Then
End If ' If concatLine <> "" Then
Next j
3) or better yet, concat string until you reach the end of the record and then split that concatenated string.
DIM concatLine As String
DIM detailsRecord as Integer
For detailsRecord = 1 To UBound(aLineItem)
if LCase(Right(aLineItem(detailsRecord),5)) = 'wo_no' Then Exit For ' when we find this tag, we know where details record begin
Next detailsRecord
For j = detailsRecord + 1 To UBound(aLineItem) ' begin looping detail records
concatLine = concatLine + aLineItem(j)
If UCase(Right(aLineItem(j), 5)) = "STOCK" Then ' this is your end of the record indicator
If blah = 1 Then
Debug.Print ("...." & concatLine)
End If
aItem = Split(concatLine, "~")
If (aItem(0) <> "") Then
Item.PO_num = GetWo(aItem(1))
If Item.PO_num <> "0" Then
Item.Company = aItem(1)
Item.Delivery_date = aItem(12)
Item.pn = aItem(2)
Item.QTY_ordered = aItem(6)
Item.Unit_Price = aItem(9)
End If 'If Item.PO_num <> "0" Then
End If 'If aItem(1) <> "" Then
End If ' If concatLine <> "" Then
concatLine = ""
Next j
For cases 2 and 3, don't forget to handle the headers first- you will have to move If (aItem(0) <> "") And (aItem(0) <> "included_po") And (aItem(0) <> "po_no") outside the main loop to handle header records (i believe the first 2 or records are headers).
EDIT #3:
I fixed scenario #3 to skip through the header records (assuming 'wo_no' is an indicator of the end of that record), then to concatenate strings to form a single record by searching for an end tag ("STOCK"). This method is going to be flexible enough to handle dynamic number of vbCrLfs in the email body that splits a single record into unpredictable number of strings.
The code was typed in the browser, so I don't guarantee it will work :)
Since you know what should start each line, then use the Split function using "301993959".
Dim LineItems(100) As String
x = Split(sMsg(i), "301993959")
For j = 1 To UBound(x) - 1 'Don't use 0 becuase you don't need that part
LineItems(j - 1) = x(j)
Next i
Of Course you could take it one step further by then splitting each item in the string using "~" as your second delimiter.
Dim LineItems(100, 15) As String
x = Split(Text1.Text, "301993959")
For j = 1 To UBound(x) - 1 'Don't use 0 becuase you don't need that part
y = Split(x(j), "~")
For k = 1 To 15
LineItems(j - 1, k) = y(k)
Next k
Next j

Resources