Change color of text in a TStringGrid cell - delphi-xe6

How can I change the color of text in a TStringGrid cell depending on certain conditions?
I am using TStringGrid to display a monthly calendar view on a form and I'm populating the TStringGrid with days of the month in certain rows and columns, with days of the week as column headings. I'm also populating the TStringGrid with job work orders for certain dates that are based on entries in a database. So I'm using the DrawCell event to display the content in the TStringGrid. Certain jobs are recurring jobs and other jobs are one offs. I'd like the recurring jobs to appear in one color and the one offs in another.
Is this possible, and/or should I be using a different component to accomplish this task? I assume it's not possible to have two different text colors in the same cell.
type
TCalendarView2 = class(TForm)
CalViewStringGrid: TStringGrid;
NextBtn: TButton;
PrevBtn: TButton;
MonthLabel1: TLabel;
CloseBtn: TButton;
procedure OnShow(Sender: TObject);
procedure CalViewStringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure NextBtnClick(Sender: TObject);
procedure PrevBtnClick(Sender: TObject);
procedure CloseBtnClick(Sender: TObject);
private
{ Private declarations }
FDateTime: TDateTime;
FDay: Word;
EndDate, StartDay: TDateTime; // selected date so we know what month the calendar is for
iNumDays, iDay: Integer; // Holds the number of days for a given month
procedure FillWithWorkOrders;
procedure UpdateRowHeights;
public
{ Public declarations }
MyDate : TDateTime;
end;
var
CalendarView2: TCalendarView2;
implementation
{$R *.dfm}
uses POEData;
procedure TCalendarView2.OnShow(Sender: TObject);
var
wYear, wMonth: Word;
begin
FDateTime := Date;
// Extract the month, day and year for the current date
DecodeDate (FDateTime, wYear, wMonth, FDay);
MonthLabel1.Caption := FormatSettings.LongMonthNames[wMonth] + ' ' + IntToStr(wYear);
FillWithWorkOrders;
end;
procedure TCalendarView2.CloseBtnClick(Sender: TObject);
begin
CalendarView2.Close;
end;
procedure TCalendarView2.CalViewStringGridDrawCell(Sender: TObject; ACol,
ARow: Integer; Rect: TRect; State: TGridDrawState);
var
s, ds, sDay, WorkOrder, WorkOrders: string;
dd, idx: integer;
dtDate: TDateTime;
SerType, WoNum, ETips: string;
bIsToday: boolean;
begin
s := CalViewStringGrid.Cells[ACol, ARow];
Inc(Rect.Left, 2);
Inc(Rect.Top, 2);
if (gdFixed in State) then
begin
CalViewStringGrid.Canvas.Brush.Color := CalViewStringGrid.FixedColor;
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
CalViewStringGrid.Canvas.FillRect(Rect);
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, s);
Exit;
end;
idx := Pos(#10, s);
if idx <> 0 then
begin
sDay := Copy(s, 1, idx-1);
WorkOrders := Copy(s, idx+1, MaxInt);
end else
begin
ds := s;
WorkOrders := '';
end;
if sDay <> '' then
begin
dd := StrToIntDef(sDay, 0);
dtDate := Date;
bIsToday := (MonthOf(dtDate) = MonthOf(FDateTime)) and (DayOf(dtDate) = dd);
end else begin
bIsToday := False;
end;
if bIsToday then
begin
CalViewStringGrid.Canvas.Brush.Color := clSkyBlue;
CalViewStringGrid.Canvas.Font.Color := clBlue;
end;
begin
CalViewStringGrid.Canvas.Brush.Color := CalViewStringGrid.Color;
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
end;
CalViewStringGrid.Canvas.FillRect(Rect);
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, sDay);
if (WorkOrders = '') then Exit;
Inc(Rect.Top, CalViewStringGrid.Canvas.TextHeight(sDay) + 2);
repeat
idx := Pos(#10, WorkOrders);
if idx <> 0 then
begin
WorkOrder := Copy(WorkOrders, 1, idx-1);
WorkOrders := Copy(WorkOrders, idx+1, MaxInt);
end else
begin
WorkOrder := WorkOrders;
WorkOrders := '';
end;
s := WorkOrder;
idx := Pos('-', s);
ETips := Copy(s, 1, idx-1);
s := Copy(s, idx+1, MaxInt);
idx := Pos('-', s);
SerType := Copy(s, 1, idx-1);
s := Copy(s, idx+1, MaxInt);
WoNum := s;
if bIsToday then
begin
CalViewStringGrid.Canvas.Brush.Color := clSkyBlue;
//CalViewStringGrid.Font.Color := clBlue;
end
else if SerType = 'R' then
begin
CalViewStringGrid.Canvas.Font.Color := clRed;
end
else if SerType = 'P' then
begin
CalViewStringGrid.Canvas.Font.Color := clBlue;
end
else if SerType = 'S' then
begin
CalViewStringGrid.Canvas.Font.Color := clGreen;
end
else if SerType = 'N' then
begin
CalViewStringGrid.Canvas.Font.Color := clBlack;
end;
begin
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
end;
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, WorkOrder);
Inc(Rect.Top, CalViewStringGrid.Canvas.TextHeight(WorkOrder) + 2);
until WorkOrders = '';
// CalViewStringGrid.Canvas.Font.Color := clBlack;
end;
procedure TCalendarView2.FillWithWorkOrders;
const
days: array[0..6] of String = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
var
X, Y, i, DateSW, RotType, PurType, SheType, SW, iNumDays: Integer;
dtTime, StartDay, EndDate: TDateTime;
SerType, WoNum, CoName, SCity, ETips, s: string;
wDay: Word;
WorkOrders: array[1..31] of String;
begin
RotType := 0;
PurType := 0;
SheType := 0;
SW := 0;
// This section displays the abbreviated day of the week in each cell in the first row,
// and clears out cell info just in case any data was left over from before
for i := 0 to 6 do
begin
CalViewStringGrid.Cells[i, 0] := days[i];
CalViewStringGrid.Cells[i, 1] := '';
CalViewStringGrid.Cells[i, 2] := '';
CalViewStringGrid.Cells[i, 3] := '';
CalViewStringGrid.Cells[i, 4] := '';
CalViewStringGrid.Cells[i, 5] := '';
CalViewStringGrid.Cells[i, 6] := '';
end;
// Gets the number of days for the current month
iNumDays := DaysInMonth(FDateTime);
// The next two lines initialize the variables the first time through
if DateSW = 0 then
begin
StartDay := FDateTime - FDay;
EndDate := EndOfTheMonth(FDateTime);
end;
DateSW := 1;
//Generate and open the ToBeSchedGrid Query
POE_Data.ToBeSchedGrid.Close;
POE_Data.ToBeSchedGrid.Sql.Clear;
POE_Data.ToBeSchedGrid.Sql.Add('SELECT DISTINCT D.WorkOrder, D.CustID, D.OpID, D.EnteredDate, D.EnteredTime, D.EstServiceDate, D.Status, D.EstBoxes, D.Truck, D.EstTips, D.ServiceDesc, D.Zone, D1.CompanyName, D1.Contact, D1.SContact1, D1.SPhone1, D1.SCity');
POE_Data.ToBeSchedGrid.Sql.Add('FROM ":Shred:WorkOrdersIn.DB" D, ":Shred:Customer.DB" D1');
POE_Data.ToBeSchedGrid.Sql.Add('WHERE (D.EstServiceDate > "' + DateToStr(StartDay) + '")');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D.EstServiceDate <= "' + DateToStr(EndDate) + '")');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D1.CustID = D.CustID)');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D.Status <> "Cancelled")');
POE_Data.ToBeSchedGrid.Sql.Add('ORDER BY D.EstServiceDate');
// Save this Query to a text file for debugging purposes
POE_Data.ToBeSchedGrid.Sql.SaveToFile('c:\PolarQBE\WorkOrdersIn.txt');
POE_Data.ToBeSchedGrid.Open;
// populate each day's Work Orders
While NOT POE_Data.ToBeSchedGrid.EOF do
begin
dtTime := POE_Data.ToBeSchedGridEstServiceDate.AsDateTime;
SerType := POE_Data.ToBeSchedGridServiceDesc.AsString;
WoNum := POE_Data.ToBeSchedGridWorkOrder.AsString;
SCity := POE_Data.ToBeSchedGridSCity.AsString;
ETips := POE_Data.ToBeSchedGridEstTips.AsString;
if ETips = '' then ETips := '0';
CoName := POE_Data.ToBeSchedGridCompanyName.AsString;
if SerType = 'Route' then
Inc(RotType);
if SerType = 'Purge' then
Inc(PurType);
if SerType = 'Shred Event' then
Inc(SheType);
//wDay := DayOfTheMonth(FDateTime);
wDay := DayOfTheMonth(dtTime);
//WorkOrders[wDay] := WorkOrders[wDay] + ETips + '-' + Copy(CoName,1,11) + '-' + Copy(SCity,1,8) + '-' + Copy(SerType,1,1) + '-' + WoNum + #10;
WorkOrders[wDay] := WorkOrders[wDay] + ETips + '-' + Copy(SerType,1,1) + '-' + WoNum + #10;
POE_Data.ToBeSchedGrid.Next;
end;
// Initialize the Row and Column counters
Y := 1;
X := DayOfWeek(StartOfTheMonth(FDateTime)- 1);
if X > 6 then X := (X div 6) - 1;
for i := 1 to iNumDays do
begin
s := IntToStr(i);
if WorkOrders[i] <> '' then begin
s := s + #10 + WorkOrders[i];
end;
CalViewStringGrid.Cells[X, Y] := s;
// increment the column counter
Inc(X);
// if the column counter is greater than 6 reset back to 0.
if X > 6 then
begin
X := 0;
Inc(Y);
end;
end;
UpdateRowHeights;
end;
procedure TCalendarView2.UpdateRowHeights;
var
X, Y, TxtHeight: Integer;
MaxHeight: Integer;
R: TRect;
begin
// This next line seems to really control the height of the rows
CalViewStringGrid.Canvas.Font.Size := 8;
for Y := CalViewStringGrid.FixedRows to CalViewStringGrid.RowCount - 1 do
begin
MaxHeight := CalViewStringGrid.DefaultRowHeight - 4;
for X := CalViewStringGrid.FixedCols to CalViewStringGrid.ColCount - 1 do
begin
R := Rect(0, 0, CalViewStringGrid.ColWidths[X] - 4, 0);
TxtHeight := DrawText(CalViewStringGrid.Canvas.Handle,
PChar(CalViewStringGrid.Cells[X, Y]), -1, R, DT_WORDBREAK or DT_CALCRECT);
if TxtHeight > MaxHeight then
MaxHeight := TxtHeight;
end;
// 11/18/2015 - was = AGrid.RowHeights[Y] := MaxHeight + 4;
CalViewStringGrid.RowHeights[Y] := MaxHeight + 1;
end;
end;

Yes, it is possible to use multiple colors in a single cell. Since you are already using the TStringGrid.OnDrawCell event to draw the cells yourself, simply extend your drawing logic to include per-job text colors. All you have to do is assign the TStringGrid.Canvas.Font.Color property before drawing a job's text onto the TStringGrid.Canvas. You just need to expose a way for your OnDrawCell handler to know when a given job is recurring or not, so it can assign the appropriate color before drawing that job's text.
Update: Try something more like this instead:
type
TCalViewForm = class(TForm)
CalViewStringGrid: TStringGrid;
procedure OnShow(Sender: TObject);
procedure CalViewStringGridDrawCell(Sender: TObject; ACol,
private
FDateTime: TDateTime;
FDay: Word;
procedure FillWithWorkOrders;
procedure UpdateRowHeights;
end;
...
procedure TCalViewForm.OnShow(Sender: TObject);
var
wYear, wMonth: Word;
begin
FDateTime := Date;
// Extract the month, day and year for the current date
DecodeDate (FDateTime, wYear, wMonth, FDay);
MonthLabel.Caption := FormatSettings.LongMonthNames[wMonth] + ' ' + IntToStr(wYear);
FillWithWorkOrders;
end;
procedure TCalViewForm.FillWithWorkOrders;
const
days: array[0..6] = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
var
X, Y, i, DateSW: Integer;
dtTime: TDateTime;
SerType, WoNum, CoName, SCity, ETips, s: string;
wDay: Word;
WorkOrders: array[1..31] of String;
begin
RotType := 0;
PurType := 0;
SheType := 0;
SW := 0;
// This section displays the abbreviated day of the week in each cell in the first row,
// and clears out cell info just in case any data was left over from before
for i := 0 to 6 do
begin
CalViewStringGrid.Cells[i, 0] := days[i];
CalViewStringGrid.Cells[i, 1] := '';
CalViewStringGrid.Cells[i, 2] := '';
CalViewStringGrid.Cells[i, 3] := '';
CalViewStringGrid.Cells[i, 4] := '';
CalViewStringGrid.Cells[i, 5] := '';
CalViewStringGrid.Cells[i, 6] := '';
end;
// Gets the number of days for the current month
iNumDays := DaysInMonth(FDateTime);
// The next two lines initialize the variables the first time through
if DateSW = 0 then
begin
StartDay := FDateTime - FDay;
EndDate := EndOfTheMonth(FDateTime);
end;
DateSW := 1;
//Generate and open the ToBeSchedGrid Query
POE_Data.ToBeSchedGrid.Close;
POE_Data.ToBeSchedGrid.Sql.Clear;
POE_Data.ToBeSchedGrid.Sql.Add('SELECT DISTINCT D.WorkOrder, D.CustID, D.OpID, D.EnteredDate, D.EnteredTime, D.EstServiceDate, D.Status, D.EstBoxes, D.Truck, D.EstTips, D.ServiceDesc, D.Zone, D1.CompanyName, D1.Contact, D1.SContact1, D1.SPhone1, D1.SCity');
POE_Data.ToBeSchedGrid.Sql.Add('FROM ":Shred:WorkOrdersIn.DB" D, ":Shred:Customer.DB" D1');
POE_Data.ToBeSchedGrid.Sql.Add('WHERE (D.EstServiceDate > "' + DateToStr(StartDay) + '")');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D.EstServiceDate <= "' + DateToStr(EndDate) + '")');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D1.CustID = D.CustID)');
POE_Data.ToBeSchedGrid.Sql.Add('AND (D.Status <> "Cancelled")');
POE_Data.ToBeSchedGrid.Sql.Add('ORDER BY D.EstServiceDate');
// Save this Query to a text file for debugging purposes
POE_Data.ToBeSchedGrid.Sql.SaveToFile('c:\PolarQBE\WorkOrdersIn.txt');
POE_Data.ToBeSchedGrid.Open;
// populate each day's Work Orders
While NOT POE_Data.ToBeSchedGrid.EOF do
begin
dtTime := POE_Data.ToBeSchedGridEstServiceDate.AsDateTime;
SerType := POE_Data.ToBeSchedGridServiceDesc.AsString;
WoNum := POE_Data.ToBeSchedGridWorkOrder.AsString;
SCity := POE_Data.ToBeSchedGridSCity.AsString;
ETips := POE_Data.ToBeSchedGridEstTips.AsString;
if ETips = '' then ETips := '0';
CoName := POE_Data.ToBeSchedGridCompanyName.AsString;
if SerType = 'Route' then
Inc(RotType);
if SerType = 'Purge' then
Inc(PurType);
if SerType = 'Shred Event' then
Inc(SheType);
wDay := DayOfTheMonth(dtTime);
//WorkOrders[wDay] := WorkOrders[wDay] + ETips + '-' + Copy(CoName,1,11) + '-' + Copy(SCity,1,8) + '-' + Copy(SerType,1,1) + '-' + WoNum + #10;
WorkOrders[wDay] := WorkOrders[wDay] + ETips + '-' + Copy(SerType,1,1) + '-' + WoNum + #10;
POE_Data.ToBeSchedGrid.Next;
end;
// Initialize the Row and Column counters
Y := 1;
X := DayOfWeek(StartOfTheMonth(FDateTime)- 1);
if X > 6 then X := (X div 6) - 1;
for i := 1 to iNumDays do
begin
s := IntToStr(i);
if WorkOrders[i] <> '' then begin
s := s + #10 + WorkOrders[i];
end;
CalViewStringGrid.Cells[X, Y] := s;
// increment the column counter
Inc(X);
// if the column counter is greater than 6 reset back to 0.
if X > 6 then
begin
X := 0;
Inc(Y);
end;
end;
UpdateRowHeights;
end;
procedure TCalViewForm.CalViewStringGridDrawCell(Sender: TObject; ACol,
ARow: Integer; Rect: TRect; State: TGridDrawState);
var
s, sDay, WorkOrder, WorkOrders: string;
dd, idx: integer;
dtDate: TDateTime;
SerType, WoNum, ETips: string;
bIsToday: boolean;
begin
s := CalViewStringGrid.Cells[ACol, ARow];
Inc(Rect.Left, 2);
Inc(Rect.Top, 2);
if (gdFixed in State) then
begin
CalViewStringGrid.Canvas.Brush.Color := CalViewStringGrid.FixedColor;
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
CalViewStringGrid.Canvas.FillRect(Rect);
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, s);
Exit;
end;
idx := Pos(#10, s);
if idx <> 0 then
begin
sDay := Copy(s, 1, idx-1);
WorkOrders := Copy(s, idx+1, MaxInt);
end else
begin
sDay := s;
WorkOrders := '';
end;
if sDay <> '' then
begin
dd := StrToIntDef(sDay, 0);
dtDate := Date;
bIsToday := (MonthOf(dtDate) = MonthOf(FDateTime)) and (DayOf(dtDate) = dd);
end else begin
bIsToday := False;
end;
if bIsToday then
begin
CalViewStringGrid.Canvas.Brush.Color := clSkyBlue;
CalViewStringGrid.Canvas.Font.Color := clBlue;
end
begin
CalViewStringGrid.Canvas.Brush.Color := CalViewStringGrid.Color;
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
end;
CalViewStringGrid.Canvas.FillRect(Rect);
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, sDay);
if (WorkOrders = '') then Exit;
Inc(Rect.Top, CalViewStringGrid.Canvas.TextHeight(sDay) + 2);
repeat
idx := Pos(#10, WorkOrders);
if idx <> 0 then
begin
WorkOrder := Copy(WorkOrders, 1, idx-1);
WorkOrders := Copy(WorkOrders, idx+1, MaxInt);
end else
begin
WorkOrder := WorkOrders;
WorkOrders := '';
end;
s := WorkOrder;
idx := Pos('-', s);
ETips := Copy(s, 1, idx-1);
s := Copy(s, idx+1, MaxInt);
idx := Pos('-', s);
SerType := Copy(s, 1, idx-1);
s := Copy(s, idx+1, MaxInt);
WoNum := s;
if SerType = 'R' then
begin
CalViewStringGrid.Canvas.Font.Color := clRed;
end
else if SerType = 'P' then
begin
CalViewStringGrid.Canvas.Font.Color := clBlue;
end
else if SerType = 'S' then
begin
CalViewStringGrid.Canvas.Font.Color := clGreen;
end
else if bIsToday then
begin
CalViewStringGrid.Canvas.Font.Color := clBlue;
end
begin
CalViewStringGrid.Canvas.Font.Color := CalViewStringGrid.Font.Color;
end;
CalViewStringGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, WorkOrder);
Inc(Rect.Top, CalViewStringGrid.Canvas.TextHeight(WorkOrder) + 2);
until WorkOrders = '';
end;
procedure TCalViewForm.UpdateRowHeights;
var
X, Y, TxtHeight: Integer;
MaxHeight: Integer;
R: TRect;
begin
// This next line seems to really control the height of the rows
CalViewStringGrid.Canvas.Font.Size := 9;
for Y := CalViewStringGrid.FixedRows to CalViewStringGrid.RowCount - 1 do
begin
MaxHeight := CalViewStringGrid.DefaultRowHeight - 4;
for X := CalViewStringGrid.FixedCols to CalViewStringGrid.ColCount - 1 do
begin
R := Rect(0, 0, CalViewStringGrid.ColWidths[X] - 4, 0);
TxtHeight := DrawText(CalViewStringGrid.Canvas.Handle,
PChar(CalViewStringGrid.Cells[X, Y]), -1, R, DT_WORDBREAK or DT_CALCRECT);
if TxtHeight > MaxHeight then
MaxHeight := TxtHeight;
end;
// 11/18/2015 - was = AGrid.RowHeights[Y] := MaxHeight + 4;
CalViewStringGrid.RowHeights[Y] := MaxHeight + 1;
end;
end;

Related

ORA-06502: PL/SQL: numeric or value error: character string buffer too small only three numbers

create or replace FUNCTION "FNC_CALCULATE_MOD11" (P_VALOR IN NUMBER)
return number is
Result number;
begin
DECLARE
-- LOCAL VARIABLES HERE
V_PROCESSO VARCHAR2(30);
V_PESO NUMBER := 2;
V_SOMA NUMBER := 0;
V_RESTO NUMBER := 0;
BEGIN
V_PROCESSO := TO_CHAR(P_VALOR);
WHILE LENGTH(V_PROCESSO) < 6 --Popular com zeros no inicio até 6
LOOP
V_PROCESSO := '0'||V_PROCESSO;
END LOOP;
--accuses error on this line
FOR I IN REVERSE 1 .. LENGTH(V_PROCESSO)
LOOP
V_SOMA := TO_CHAR (V_SOMA) + TO_NUMBER(SUBSTR(V_PROCESSO,i,1))*V_PESO;
IF V_PESO = 9 THEN --repetir peso se for necessario
V_PESO := 2;
ELSE
V_PESO := V_PESO + 1;
END IF;
END LOOP;
V_RESTO := MOD(V_SOMA, 11);
Result := 11 - V_RESTO;
IF ((Result = 0) OR (Result = 1) OR (Result >= 10)) THEN
Result := 1;
END IF;
END;
return(Result);
end FNC_CALCULATE_MOD11;
Try to change V_PROCESSO to a bigger size, for example V_PROCESSO VARCHAR2(300);

Oracle how to instr for tab or end of line using regexp_instr

I have the following code
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
l_field_start := l_field_end + 1;
exit when l_field_end = 0;
end loop;
dbms_output.put_line('');
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;
with which I'm trying to parse this clob test data:
LINE_TEXT
"PERSON_ID_NUMBER 30000 1223445454"
"PERSON_DOB 30000 01-01-1900"
The clob data is tab separated and has a chr(10) at the end. I'm not familiar with regexp_instr, but currently I'm only using instr to search for the tab separators; so it's missing the end of line field and producing:
PERSON_ID_NUMBER,30000,,
PERSON_DOB,30000,,
How can I change the instr into a regexp_instr to also look for the end of line character in addition to the tab and then correctly pick up the last field?
I need the function to be performant, since it is parsing large files.
You can split the line of CLOB column by converting to char, and then apply regexp_substr() as
with t as
(
select level as row_num, regexp_substr(to_char(t.line_text),'^.*$',1,level,'m') as str
from tab t
connect by level <= length (to_char(t.line_text))
- length (replace (to_char(t.line_text), chr (10))) + 1
)
select row_num, regexp_replace(str,'[[:space:]]+',',') as str
from t;
ROW_NUM STR
------- -----------------------------------------
1 PERSON_ID_NUMBER,30000,1223445454
2 PERSON_DOB,30000,01-01-1900
Demo
Edit : even works without to_char() conversion, if your CLOB is huge then you need to split step by step by substr(str,1,4000), substr(str,4001,8000) ...
with t as
(
select level as row_num, regexp_substr(substr(t.line_text,1,4000),'^.*$',1,level,'m') str
from tab t
connect by level <= length (substr(t.line_text,1,4000))
- length (replace(substr(t.line_text,1,4000), chr (10))) + 1
)
select row_num, regexp_replace(substr(str,1,4000),'[[:space:]]+',',') as str
from t
Fixed it with:
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
exit when l_field_end = 0;
l_field_start := l_field_end + 1;
end loop;
l_field := substr(l_line, l_field_start);
dbms_output.put_line(l_field);
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;

Getting debug error "; expected" after adding new ifclause to procedure

After adding the ifclause for Art to a MasterReport to multiple the Netto total by -1 for get a -ve result in the credit invoice, I am getting a ; expected with the error indicator jumping to the procedure after it.
Image of Error Message
It's likely a very stupid error but I can't wrap my head around the problem at the moment. Commenting the new ifclause out will resolve the issue so it's something with that.
procedure FooterSR1OnBeforePrint(Sender: TfrxComponent);
begin
if ( <frxdsqryKopfdaten."MwStNichtAusweisbar"> = 0 ) then
mmoBrutto.Memo.Text := FormatFloat('#,##0.00', EndBrutto ) + ' ' + <frxdsqryKopfdaten."CurrencyString">
else
mmoBrutto.Memo.Text := FormatFloat('#,##0.00', EndNetto ) + ' ' + <frxdsqryKopfdaten."CurrencyString">;
end;
procedure Hauptkopf1OnBeforePrint(Sender: TfrxComponent);
begin
if (<frxdsqryArt."Art"> = 'Gutschrift') then
begin
EndNetto := EndNetto * (-1)
end else begin
EndNetto := EndNetto;
end;
end;
begin
mmoNetto.Memo.Text := FormatFloat('#,##0.00', EndNetto ) + ' ' + frxdsqryKopfdaten."CurrencyString">
end;
procedure BandBankOnBeforePrint(Sender: TfrxComponent);
begin
if <frxdsqryFirma."Bank1"> <> '' then
mmoBank.Visible := False
else
mmoBank.Visible := FALSE;
if <frxdsqryFirma."IBAN1"> <> '' then
begin
mmoIBAN.Visible := FALSE;
mmoBank.Visible := FALSE;
mmoBank.Height := 0
end else begin
mmoIBAN.Visible := FALSE;
mmoBank.Visible := FALSE;
end;
end;
This is wrong (4× begin, but only 3× end):
procedure Hauptkopf1OnBeforePrint(Sender: TfrxComponent);
begin
if (<frxdsqryArt."Art"> = 'Gutschrift') then
begin
EndNetto := EndNetto * (-1)
end else begin
EndNetto := EndNetto;
end;
end;
begin
mmoNetto.Memo.Text := FormatFloat('#,##0.00', EndNetto ) + ' ' + frxdsqryKopfdaten."CurrencyString">
end;
Your probably wanted this (but hard to say):
procedure Hauptkopf1OnBeforePrint(Sender: TfrxComponent);
begin
if (<frxdsqryArt."Art"> = 'Gutschrift') then
begin
EndNetto := EndNetto * (-1)
end else begin
EndNetto := EndNetto;
end;
end;
mmoNetto.Memo.Text := FormatFloat('#,##0.00', EndNetto ) + ' ' + frxdsqryKopfdaten."CurrencyString">
end;

PL-00215: varchar2 length error

I'm trying to do a very simple thing: extract a date and do some controls on it.
Everything's fine but when I try to compile this error pops out and I really don't have a clue about how to solve it...
This is the code:
function get_crediti_formativi_biennio(p_userid varchar2,
p_prof_abil varchar2,
p_id_persona number,
p_tipo_formazione number,
p_anno_formazione varchar2,
p_flag_calcolo number,
p_crediti_totali out number)
return varchar2 is
v_count number;
v_anno varchar2;
v_biennio_from number;
v_biennio_to number;
l_err_msg varchar2(1024) := '+OK0000 Operazione effettuata con successo';
begin
v_count := null;
begin
select TO_CHAR(ap.valore)
into v_anno
from intc_attr_persone ap
where ap.id_attributo = 202
and ap.id = p_id_persona;
exception
when no_data_found then
v_anno := '';
end;
if v_anno >= 2015 and (mod(v_anno, 2) != 0) then
if (mod(p_anno_formazione, 2) = 0) then
v_biennio_from := p_anno_formazione;
v_biennio_to := p_anno_formazione + 1;
else
v_biennio_from := p_anno_formazione - 1;
v_biennio_to := p_anno_formazione;
end if;
else
if v_anno < 2015 or v_anno = '' or v_anno is null then
if (mod(p_anno_formazione, 2) = 0) then
v_biennio_from := p_anno_formazione - 1;
v_biennio_to := p_anno_formazione;
else
v_biennio_from := p_anno_formazione;
v_biennio_to := p_anno_formazione + 1;
end if;
end if;
end if;
The problem is on v_anno.....

PL/SQL CRUD matrix from a source code

I am trying to create a CRUD matrix for my function from a source code of that function, so I created a procedure that will read a source code.
create or replace procedure test_
IS
CURSOR c_text is
SELECT USER_SOURCE.TEXT
FROM USER_SOURCE
WHERE USER_SOURCE.name='TEST_FUNCTION'
AND USER_SOURCE.type='FUNCTION';
order by line;
v_single_text varchar2(4000);
v_tmp_text varchar2(10000) := ' ';
begin
open c_text;
loop
fetch c_text into v_single_text;
exit when c_text%notfound;
v_tmp_text := v_tmp_text|| chr(10) || rtrim(v_single_text);
dbms_output.put_line(v_single_text);
end loop;
close c_text;
end test_;
And that works very good for me, I get the source code of my desired function. It's a very simple function and I use this to learn PL/SQL. Output of that procedure look's like this.
function test_funkction Return varchar2
IS
kpp_value varchar2(20);
begin
select KPP
into kpp_value
from CUSTOMER
where CUSTOMER_ID = 200713;
dbms_output.put_line (kpp_value);
Return kpp_value;
end test_function;
Now, how to parse the string I've got in the output to get a desired result, my result should be like this
==TABLE_NAME==========OPERATIONS==
CUSTOMER - R - -
==================================
Now I have managed to do it.
But it will only work with my simple function, now I want to make a procedure that will work with any function.
Source code below.
create or replace procedure test_
IS
v_string_fnc varchar2(10000) := UPPER('function test_function
Return varchar2
IS
kpp_value varchar2(20);
begin
select KPP into kpp_value from CUSTOMER where CUSTOMER_ID = 200713;
dbms_output.put_line (kpp_value);
Return kpp_value;
end test_function;');
v_check PLS_INTEGER;
CURSOR c_text is
SELECT USER_SOURCE.TEXT
FROM USER_SOURCE
WHERE USER_SOURCE.name = 'TEST_FUNCTION'
AND USER_SOURCE.type = 'FUNCTION'
order by line;
v_single_text varchar2(4000);
v_tmp_text varchar2(10000) := ' ';
/*v_string varchar2(10000);*/
insert_flag char := '-';
read_flag char := '-';
update_flag char := '-';
delete_flag char := '-';
empty_space char(34) := ' ';
underline char(42) := '==========================================';
/*v_txt varchar2(10000) := ' ';*/
result_table varchar2(1000) := '/';
begin
open c_text;
loop
fetch c_text
into v_single_text;
exit when c_text%notfound;
v_tmp_text := v_tmp_text || chr(10) || rtrim(v_single_text);
/* print source code*/
/*dbms_output.put_line(v_single_text);*/
end loop;
close c_text;
/*DELETE SEARCH*/
v_check := instr(v_string_fnc, 'DELETE ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO DELETE COMMAND');
else
dbms_output.put_line('THERE IS A DELETE COMMAND');
delete_flag := 'D';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY DELETE: ' || result_table);
end if;
/*SELECT SEARCH*/
v_check := instr(v_string_fnc, 'SELECT ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO READ COMMAND');
else
dbms_output.put_line('THERE IS A READ COMMAND');
read_flag := 'R';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY READ: ' || result_table);
end if;
/*UPDATE SEARCH*/
v_check := instr(v_string_fnc, 'UPDATE ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO UPDATE COMMAND');
else
dbms_output.put_line('THERE IS A UPDATE COMMAND');
update_flag := 'U';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY UPDATE: ' || result_table);
end if;
/*INSERT SEARCH*/
v_check := instr(v_string_fnc, 'INSERT ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO CREATE COMMAND');
else
dbms_output.put_line('THERE IS A CREATE COMMAND');
insert_flag := 'C';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY CREATE: ' || result_table);
end if;
dbms_output.put_line(' ');
dbms_output.put_line('==========' || 'TABLE_NAME' || '==========' ||
'OPERATIONS' || '==');
dbms_output.put_line(empty_space || insert_flag || read_flag ||
update_flag || delete_flag);
dbms_output.put_line(underline);
end test_;
With that procedure I can extract and output my code, dbms needs a bit clean up but it will give the result I need.
Now a few questions, how to put a source code of my function to a variable that is not predefined, here is v_string_fnc but it needs to be predefined to work.
And how to link a certain operation with the table, here in my example is easy, one SELECT and keyword FROM that gives me a name of table.
Struggling continues
The bigger part it's done, just a tuning after this.
v_check := instr2(v_string_fnc, 'DROP ');
if v_check > 0 then
delete_flag := 'D';
v_check := instr2(v_string_fnc, 'TABLE ', v_check);
v_check := v_check + 6;
result_table := substr(v_string_fnc, v_check);
rest_string := result_table;
result_table := substr(result_table, 0, instr(result_table, ' '));
result_table := rtrim(result_table);
result_table := rtrim(result_table, ';');
merge into result_set
using dual
on (tables_used = result_table)
when matched then
update set drop_operation = delete_flag
when not matched then
insert
(tables_used, drop_operation)
values
(result_table, delete_flag);
while v_check > 0 loop
v_check := instr2(rest_string, 'DROP ');
if v_check > 0 then
delete_flag := 'D';
v_check := instr2(rest_string, 'TABLE ', v_check);
v_check := v_check + 6;
result_table := substr(rest_string, v_check);
rest_string := result_table;
result_table := substr(result_table, 0, instr(result_table, ' '));
result_table := rtrim(result_table);
result_table := rtrim(result_table, ';');
merge into result_set
using dual
on (tables_used = result_table)
when matched then
update set drop_operation = delete_flag
when not matched then
insert
(tables_used, drop_operation)
values
(result_table, delete_flag);
end if;
end loop;
end if;