Change "Expiration Date" font color if within 30 days or already expired? - asp.net

Not sure the best approach to do this, the application is older which is why I'm having so much trouble generating this. I read about doing a CASE statement but I don't have much SQL experience. Any help is appreciated and answers will be respected. Thanks. Also, there's no design to this, the people who wrote the application used placeholders and all the data comes form this huge file, which is beyond me. I don't know why because I've never seen anything like this. It's a monster.
'-
Dim TemplateColumnCDLExpiration As System.Web.UI.WebControls.TemplateColumn
TemplateColumnCDLExpiration = New System.Web.UI.WebControls.TemplateColumn
If Me.AllowSorting Then
TemplateColumnCDLExpiration.SortExpression = "CDLExpiration"
End If
TemplateColumnCDLExpiration.ItemTemplate =
New JAG.WebUI.Controls.IEditGridTemplate(ListItemType.Item,
"CDLExpiration",
JAG.WebUI.Controls.tEditGridItemControlType.Label)
TemplateColumnCDLExpiration.HeaderText = "CDL Expiration"
MyBase.Columns.Add(TemplateColumnCDLExpiration)
'-

OK, I'll give you the answer to your CASE question, but you have to promise that you'll read the considerations below. :)
I'm using Oracle SQL; I don't know if the syntax is different for other SQL implementations. Here's an example of a dummy query to show the syntax:
SELECT
CASE
WHEN (sysdate - TO_DATE('04/09/2013', 'mm/dd/yyyy') > 30) THEN 'red'
ELSE 'black'
END text_color
FROM dual;
The code in the parenthesis after the WHEN is the test. It compares the current date to April 9th and asks, "Is April 9th more than 30 days ago?" If so, it returns 'red' as the value of text_color. If that condition is false, it returns 'black'. Here's a more generalized form:
SELECT
CASE
WHEN (sysdate - :date_to_check > :expiration_days) THEN 'red'
ELSE 'black'
END text_color
FROM :my_table;
Considerations
You don't need this nasty piece of logic in SQL. The check for X number of days passing since the given date is not database logic. Fetching a date is database logic, deciding how many days have elapsed from that date until today could be argued as either DB logic or business logic, but deciding the text color is definitely display logic, meaning you should be modifying your .NET code, not your SQL. What happens if you need to change the display colors? The date check remains the same, but you have to modify...your SQL? SQL modifications should only need to happen if the data that is being retrieved or stored is modified. The bottom line is that this is not a clean separation of concerns.

Related

Is there a way of using an IF statement inside a FOR EACH in PROGRESS-4GL?

I'm transforming some SQL codes into Progress 4GL. The specific code I'm writing right now has a lot of possible variables insertions, for example, there are 3 checkboxes that can be selected to be or not, and each selection adds a "AND ind_sit_doc IN", etc.
What I'd like to do is something like this:
FOR EACH doc-fiscal USE-INDEX dctfsc-09
WHERE doc-fiscal.dt-docto >= pd-data-1
AND doc-fiscal.dt-docto <= pd-data-2
AND doc-fiscal.cod-observa <> 4
AND doc-fiscal.tipo-nat <> 3
AND doc-fiscal.cd-situacao <> 06
AND doc-fiscal.cd-situacao <> 22
AND (IF pc-ind-sit-doc-1 = 'on' THEN: doc-fiscal.ind-sit-doc=1) NO-LOCK,
EACH natur-oper USE-INDEX natureza
WHERE doc-fiscal.nat-operacao = natur-oper.nat-operacao NO-LOCK:
The if part would only be read if the variable was in a certain way.
Is it possible?
Yes, you can do that (more or less as nwahmaet showed).
But it is a very, very bad idea in a non-trivial query. You are very likely going to force a table-scan to occur and you may very well send all of the data to the client for selection. That's going to be really painful. You would be much better off moving the IF THEN ELSE outside of the WHERE clause and implementing two distinct FOR EACH statements.
If your concern with that is that you would be duplicating the body of the FOR EACH then you could use queries. Something like this:
define query q for customer.
if someCondition = true then
open query q for each customer no-lock where state = "ma".
else
open query q for each customer no-lock where state = "nh".
get first q.
do while available customer:
display custNum state.
get next q.
end.
This is going to be much more efficient for anything other than a tiny little table.
You can also go fully dynamic and just build the needed WHERE clause as a string - but that involves using handles and is more complicated. But if that sounds attractive lookup QUERY-PREPARE in the documentation.
You can add an IF statement in a FOR EACH. You must have the complete IF ... THEN ... ELSE though.
For example:
FOR EACH customer WHERE (IF discount > 50 THEN state = 'ma' ELSE state = ''):
DISPL name state discount.
END.
That said, that condition will not be used for index selection and will only be applied on the client (if you're using networked db connections this is bad).

PeopleSoft Query Manager - string expression, trying to get the third set of ||

In PeopleSoft I am trying to get some text from the middle of a cell. I'm having trouble with creating a string expression to do this. The data in the cell looks like this (I've bolded what I'm trying to capture):
|| SVP: Person Number one|| Interview Completed by: Person Number two
|| Info: Employee lists off a bunch of stuff.
|| Non-Relevant Question? Y
|| Manager provided data to: Person Number three
I've submitted a question here and I've come close. Here's what I have so far:
substring(J.HR_NP_NOTE_TEXT,CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100),CHARINDEX('||',J.HR_NP_NOTE_TEXT)
The problem is that this is not stopping at the ||. A better solution - if I knew how - would be if I could get the text that is after the third set of '||' and stop before the fourth set of '||'.
Without seeing your actual output, I think that you will find that your issue is to do with the way substring actually works.
From the Transact SQL documentation I can see online (https://learn.microsoft.com/en-us/sql/t-sql/functions/substring-transact-sql?view=sql-server-2017)
substring takes 3 arguments:
SUBSTRING ( expression ,start , length )
you have the expression - J.HR_NP_NOTE_TEXT
you have the start - CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100) - I'm not 100% sure why you are only looking for the '|| Info:' after 100 characters though?
But then when you calculate the length - CHARINDEX('||',J.HR_NP_NOTE_TEXT) you are not going to get what you expect. Assuming that it ignores the earlier ||'s in the string (will it?), it will return a position in the string, not an actual length.
A more appropriate calculation would be something like
(CHARINDEX('||',J.HR_NP_NOTE_TEXT)-CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100))
i.e. the difference between detecting the '|| Info:' position and the next '||'.
Making your whole query:
substring(J.HR_NP_NOTE_TEXT,CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100),(CHARINDEX('||',J.HR_NP_NOTE_TEXT)-CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100)).
As I'm not entirely sure about your CHARINDEX looking for '||', I would probably try this one though - to find the next '||' after '|| Info:'
substring(J.HR_NP_NOTE_TEXT,CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100),(CHARINDEX('||',J.HR_NP_NOTE_TEXT,CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100))-CHARINDEX('|| Info:',J.HR_NP_NOTE_TEXT)+8,100)).
Although this is a question you're asking about a PS Query, you may get better answers from people who are focused primarily on the SQL language you're using too, as this is not strictly speaking a PeopleSoft only question.
Hope that helps!

How to put a part of a code as a string in table to use it in a procedure?

I'm trying to resolve below issue:
I need to prepare table that consists 3 columns:
user_id,
month
value.
Each from over 200 users has got different values of parameters that determine expected value which are: LOB, CHANNEL, SUBSIDIARY. So I decided to store it in table ASYSTENT_GOALS_SET. But I wanted to avoid multiplying rows and thought it would be nice to put all conditions as a part of the code that I would use in "where" clause further in procedure.
So, as an example - instead of multiple rows:
I created such entry:
So far I created testing table ASYSTENT_TEST (where I collect month and value for certain user). I wrote a piece of procedure where I used BULK COLLECT.
declare
type test_row is record
(
month NUMBER,
value NUMBER
);
type test_tab is table of test_row;
BULK_COLLECTOR test_tab;
p_lob varchar2(10) :='GOSP';
p_sub varchar2(14);
p_ch varchar2(10) :='BR';
begin
select subsidiary into p_sub from ASYSTENT_GOALS_SET where user_id='40001001';
execute immediate 'select mc, sum(ppln_wartosc) plan from prod_nonlife.mis_report_plans
where report_id = (select to_number(value) from prod_nonlife.view_parameters where view_name=''MIS'' and parameter_name=''MAX_REPORT_ID'')
and year=2017
and month between 7 and 9
and ppln_jsta_symbol in (:subsidiary)
and dcs_group in (:lob)
and kanal in (:channel)
group by month order by month' bulk collect into BULK_COLLECTOR
using p_sub,p_lob,p_ch;
forall x in BULK_COLLECTOR.first..BULK_COLLECTOR.last insert into ASYSTENT_TEST values BULK_COLLECTOR(x);
end;
So now when in table ASYSTENT_GOALS_SET column SUBSIDIARY (varchar) consists string 12_00_00 (which is code of one of subsidiary) everything works fine. But the problem is when user works in two subsidiaries, let say 12_00_00 and 13_00_00. I have no clue how to write it down. Should SUBSIDIARY column consist:
'12_00_00','13_00_00'
or
"12_00_00","13_00_00"
or maybe
12_00_00','13_00_00
I have tried a lot of options after digging on topics like "Deling with single/escaping/double qoutes".
Maybe I should change something in execute immediate as well?
Or maybe my approach to that issue is completely wrong from the very beginning (hopefully not :) ).
I would be grateful for support.
I didn't create the table function described here but that article inspired me to go back to try regexp_substr function again.
I changed: ppln_jsta_symbol in (:subsidiary) to
ppln_jsta_symbol in (select regexp_substr((select subsidiary from ASYSTENT_GOALS_SET where user_id=''fake_num''),''[^,]+'', 1, level) from dual
connect by regexp_substr((select subsidiary from ASYSTENT_GOALS_SET where user_id=''fake_num''), ''[^,]+'', 1, level) is not null) Now it works like a charm! Thank you #Dessma very much for your time and suggestion!
"I wanted to avoid multiplying rows and thought it would be nice to put all conditions as a part of the code that I would use in 'where' clause further in procedure"
This seems a misguided requirement. You shouldn't worry about number of rows: databases are optimized for storing and retrieving rows.
What they are not good at is dealing with "multi-value" columns. As your own solution proves, it is not nice, it is very far from nice, in fact it is a total pain in the neck. From now on, every time anybody needs to work with subsidiary they will have to invoke a function. Adding, changing or removing a user's subsidiary is much harder than it ought to be. Also there is no chance of enforcing data integrity i.e. validating that a subsidiary is valid against a reference table.
Maybe none of this matters to you. But there are very good reasons why Codd mandated "no repeating groups" as a criterion of First Normal Form, the foundation step of building a sound data model.
The correct solution, industry best practice for almost forty years, would be to recognise that SUBSIDIARY exists at a different granularity to CHANNEL and so should be stored in a separate table.

SQL - Hours of operation

I'm having a hard time wrapping my head around what seems to be a somewhat simple issue. Let's say that I have a business whose hours are 12PM - 3AM daily. Each customer gets a bonus once per day based on their initial purchase for that day. So, let's say they spend twenty bucks on their first transaction that day -- they might get a twenty percent discount on that transaction, and that's it for the day.
I'm trying to figure out the most accurate way to check the last bonus that was given and make sure that the customer is eligible for one. I can't do a simple 24-hour check, obviously, because if a customer comes in at 11 PM Monday, for instance, and again at noon Tuesday, they will not get their second bonus.
We are using a VB6 frontend for our POS, with a SQL Server 2008 R2 database. Each time a bonus is applied, it is audited on the database side, so I can easily query the last time the bonus was applied.
EDIT: I should note that, for various reasons, the solution cannot include making any changes to the structure of the database.
I'm not sure on which side (VB or SQL) you want to apply the biz logic but in either case the process should be the same: You need to persist each customer's daily hours of operation with two attributes:
Time (the time of day that they open for business)
TimeSpan (number of hours of operation)
You then check if a transaction's time is between Time and Time + TimeSpan to calculate your business logic and the customer's bonus. Both calculations are fairly trivial in VB and SQL. You just need to make sure you persist the data logically and use it consistently.
I think your answer would be cleaner if you modified it to something like:
IF #LastBonus BETWEEN #store_open AND #store_close
BEGIN
SET #BonusDue = 0
END
ELSE
BEGIN
SET #BonusDue = 1
END
where you figure the store open and close dates based on a fixed times that are added to the date part of the last bonus. Something like
Set #openTime = '12:00'
Convert(date, #LastBonus) + #openTime
And then adding the timespan (as was suggested) to get the close time. It might be a little tricky because if it's after midnight, the open time would need to be added to the previous date instead, but you could probably work this out with a CASE statement. I'd try it out myself if my baby didn't have an ear infection. Hope that is useful to you.
How about:
IF (DATEPART(dayofyear, [lastBonusTime]) <> DATEPART(dayofyear, GetDate())) ...
Where lastBonusTime is the time of the last bonus transaction ?
You can look at the problem a bit differently. If a customer is here now (GETDATE()), has it been over 24 hours since their last purchase?
So, something like
SELECT *
FROM Customers c
INNER JOIN CustomerVisits cv
ON c.CustomerId=cv.CustomerId
INNER JOIN CustomerBonus cb
ON cv.VisitId=cb.VisitId
WHERE c.CustomerId=#CustomerId
AND LastVisitDt BETWEEN
(
DATEADD(hh,12,convert(DATE, LastVisitDt))
)
AND
(
DATEADD(hh,27,convert(DATE, LastVisitDt))
)
AND DATEADD(hh,24,LastVisitDT)<=GETDATE()
I would also consider the specifics of the data--the above is NOT TUNED FOR PERFORMANCE AT ALL. I just wanted to explain my thought process.
In the interest of separating your concerns, I would add a new table, like CUSTOMER_BONUS, with these columns:
BonusStart datetime
BonusEnd datetime
CustomerID int/uniqueidentifier/whatever
TransactionID int/whatever (points to what qualified for the bonus)
When you apply a bonus for a customer for a day, write a new record into this table for the period that it applies to. Presence of a record in this table indicates that the customer is not eligible for another bonus between BonusStart and BonusEnd. When you create a new sale, look in this table. If the record exists, no bonus, but if not, apply the bonus and create a new record here.
I came up with an answer that I'm content with but it's a little kludgy and I would be more than happy to accept a more elegant answer if one is provided. Also, I haven't thoroughly tested this since it's getting late in the day, but if there are flaws in my logic, I will happily revise or accept an answer with revisions.
Basically, I'm just going to determine that the day of the week in terms of a business day is whatever day started four hours ago. This means that all the way up through 3:59 AM, "today" will be considered the day before, which is correct for these hours of operation (I'm overshooting the 3 AM closing time to account for a site deciding to stay open a little later). I then compare this span of time to the most recent time a bonus was applied to that customer's account, using the same rules. If the two match, the bonus has been applied this business day. If they are different, it has not, and the customer is eligible.
DECLARE #CustID AS int
DECLARE #LastBonus AS date
DECLARE #BonusDue AS bit
SET #LastBonus = (SELECT TOP 1 [DateTime] FROM Audit WHERE CustomerID = #CustID AND TransactionType = 'BONUS' ORDER BY [DateTime] DESC)
IF (SELECT DATEADD(hh, -4, CURRENT_TIMESTAMP)) <>
(SELECT DATEADD(hh, -4, #LastBonus))
BEGIN
SET #BonusDue = 1
END
ELSE
BEGIN
SET #BonusDue = 0
END
If I throw this in a stored procedure, I can simply throw a customer ID at it and have it spit out a bit that will show me 1 if the customer is eligible, 0 otherwise. What I don't like about it is that if a customer's hours of operation end up getting much earlier, I'll be sunk (I guess at about 7:00 AM, when simply subtracting four hours will overlap into the previous business day, but subtracting less will not be enough to reach the previous business day). So it will work for the time being, but I'd love to see a better solution.

Cast Date in Informix

I have never used Informix before and I'm trying to write a query that will return records over the last 365 days.
Here is the query I have been trying to use:
Select * from Visit where vis_mod_dt between today-365 and today;
That returns no records even though I know that there is data for the last 365 days. I am guessing that the vis_mod_dt in not a true date column, although it displays as '12/31/1899' I have tried to cast this column using:
select * from visit where date(vis_mod_dt) between today-365 and today;
This still returns no data.
Any ideas?
Informix DATE format
Be aware that the date 1899-12-31 corresponds to the internal date representation of zero (0). That is, internally, Informix stores DATE values in a 4-byte integer, and counts the number of days since 1899-12-31, so Day 1 was 1900-01-01 (and since it knows that 1900 was not a leap year, Day 60 was 1900-03-01).
That fact makes me worry about what is in your table. However, if the data in your table cannot be converted to a DATE upon request, normally you would get an error.
What is your table schema?
It would be sensible for you to establish the schema either using DB-Access and the Info/Tables option, or use DB-Schema:
dbschema -d dbase -t visit
The DB-Schema output is more suitable for adding to your question.
The query expressions using 'TODAY-365' and 'TODAY' should work fine - if there is data to select.
DBDATE environment variable
There is an environment variable, DBDATE, that you may need to set to get things to work - to convert from a string representation to dates. Since you are probably based in the UK (from your icon), then you may want and need to set the value of DBDATE to:
export DBDATE=DMY4/
This says that dates consist of the day, the month, a 4-digit year and the '/' is used as the preferred separator. You won't be surprised to learn that the presumed default value is usually 'MDY4/', for US format; I use 'Y4MD-' habitually, so I see DATE value the same as DATETIME YEAR TO DAY, which is the ISO 8601:2004 notation for a date. (It has many benefits: it is unambiguous, and naive sorting algorithms sort such dates into date order.) There's actually a lot of mechanism in the background in IDS (IBM Informix Dynamic Server - which, I assume, is the DBMS that you are using; there are some alternatives that are also Informix DBMS) such that strings with 2-digit dates will usually be converted correctly (but they are ambiguous and undesirable), and separators other than '/' will be recognized on input, but the slash will be used on 'output' (when converting DATE to string).
Information needed to improve the answer to this question - 1st Edition.
If what is here does not help, then I recommend editing your question to include:
The table schema.
A few (2-4) rows of data that you think should be selected but aren't.
Platform and version information. It can help to have the version down to the level of detail of IDS 11.50.FC4W1; occasionally it matters. Most usually, the first three digits are what affect things, of course.
If your table is big (many columns), try to select the key columns (vis_mod_dt is by far the most important one). Ideally, you won't need any scroll bars in the display.
Make sure you don't include any sensitive information.
Information needed to improve the answer to this question - 2nd Edition
I will help you if you pay attention to the questions I ask you. I cannot help you if you do not pay attention to the questions I ask. And please edit your question rather than adding information as an 'answer'.
What is the table schema? What is the output from:
SELECT t.tabid, t.tabname, c.colno, c.colname, c.coltype, c.collength
FROM "informix".systables AS t, "informix".syscolumns AS c
WHERE t.tabid = c.tabid
AND t.tabname = "visit"
ORDER BY t.tabid, c.colno;
What do you get from:
SELECT TODAY, TODAY-365 FROM "informix".systables WHERE tabid = 1;
Do you have the environment variable DBDATE set? If so, what is its value?
Do you have the environment variables CLIENT_LOCALE or DB_LOCALE set? If so, what are their values?
Which version of Informix are you using?
Which platform are you using it on?
Which language or tool are you using to run the query.
Note: if you cannot copy'n'paste the queries above, then you probably do not need to include the quoted '"informix".' attributes on the system catalog; however, as written, the queries will work on any extant Informix database - OnLine 5.x, SE 5.x or 7.x, IDS 7.x, XPS 8.x, IDS 9.x or 10.x or 11.x - and any mode of database (unlogged, logged, MODE ANSI). I'd use the JOIN notation except that some of the older versions don't support it - though you have to be on very old versions for that to be a problem.
This is a little confusing, because when I run the following, I get data:
select count(*) from visit where vis_mod_dt between "10/01/2008" and "10/01/2009"
how about unloading the table to ascii file, examine the unloaded vis_mod_dt values to see if they conform to DBDATE=MDY4 (mmddyyyy) format?.. if they do, ALTER vis_mod_dt to TYPE DATE if it's not a DATE column, then LOAD the unloaded table back in.
the: "BETWEEN today-365 AND today" part of your SELECT statement works for me in my apps.

Resources