I created an insert procedure with some constraints. Procedure created successfully but when I try to insert Im getting ORA-01858: a non-numeric character was found where a numeric was expected. Here is my code
CREATE TABLE Customers
(
IDNumber INTEGER PRIMARY KEY,
FirstName VARCHAR(50),
LastName VARCHAR(50),
DOB DATE,
Gender CHAR(10)
);
CREATE OR REPLACE PROCEDURE insertCustomer (id IN Integer, firstName IN varchar, lastName IN varchar, dob IN date, gender varchar)
AS
BEGIN
If sysdate between to_date('monday 08:00 am') and to_date('monday 05:00 pm')
or sysdate between to_date('tuesday 08:00 am') and to_date('tuesday 05:00 pm')
or sysdate between to_date('wednesday 08:00 am') and to_date('wednesday 05:00 pm')
or sysdate between to_date('thursday 08:00 am') and to_date('thursday 05:00 pm')
or sysdate between to_date('friday 10:00 am') and to_date('friday 01:00 pm')
then
INSERT INTO Customers values(id, firstName, lastName, dob, gender);
ELSE
raise_application_error(-20101, 'Can only insert during the week between normal business hours');
END if;
END insertCustomer
;
/
execute insertCustomer (18, 'Andre', 'Walker', '12-NOV-1993', 'male');
I've been stuck a while now. I'm sure in oracle that date format is correct. Any insight would be greatly appreciated
At INSERT INTO Customers values(id, firstName, lastName, dob, gender); you are trying to insert the dob as char ('12-NOV-1993'), you need to change the insert value of dob to INSERT INTO Customers values(id, firstName, lastName, TO_DATE(dob), gender); and that should do the trick.
And also as pointed out before, your to_date('monday 08:00 am') is most certainly not correct and will result in "Julian date" errors.
This here is a different way to do what you have tried to do
IF TO_CHAR(sysdate, 'D') IN (1,2,3,4) AND TO_CHAR(sysdate, 'HH:MI am') BETWEEN '08:00 am' AND '05:00 pm' THEN
INSERT...
ELSIF TO_CHAR(sysdate, 'D') = 5 AND TO_CHAR(sysdate, 'HH:MI am') BETWEEN '10:00 am' AND '01:00 pm' THEN
INSERT...
ELSE
RAISE_APPLICATION_ERROR
END IF;
where the integers represent "day of the week" and the am in TO_CHAR(sysdate, 'HH:MI am') changes to either pm or am automatically depending on the current value of sysdate.
Edit: Note that I am European and normally use the 24h format, so I have no idea how the am/pm will deal with this(only ran a small test before posting), that is up to you to test.
Related
I am looking to build a filter component where
my search comes like b_cycle_type = '1st Day Of The Month'
and in the database b_cycle_type is stored as -1,0,1,2,3,4,5
How can I prepare postgres statement
I am trying:
SELECT "customers".*
FROM "customers"
WHERE (CASE customers.b_cycle_type
WHEN -1 THEN 'Last day of the month'
WHEN 0 THEN 'Align with first'
ELSE to_char(customers.b_cycle_type, '99th') || ' Day Of The Month'
END = '27th Day Of The Month')
It's not returning any results.
This part to_char(customers.b_cycle_type, '99th') actually results in ' 27th'(to_char Docs [*6th from the bottom]) so to combat this I would use the TRIM function.
CREATE TABLE customers(
id SERIAL PRIMARY KEY,
b_cycle_type INT);
INSERT INTO customers(b_cycle_type)
SELECT * FROM generate_series(-1,30);
SELECT "customers".*
FROM "customers"
WHERE
CASE b_cycle_type
WHEN -1 THEN 'Last day of the month'
WHEN 0 THEN 'Align with first'
ELSE
TRIM(to_char(b_cycle_type, '99th')) || ' Day Of The Month'
END = '27th Day Of The Month'
Example
You can avoid the raw SQL but it's not pretty
your_variable = '27th Day Of The Month'
customer_table = Customer.arel_table
case_stmt = Arel::Case.new(customer_table[:b_cycle_type])
.when(-1).then(Arel.sql("'Last day of the month'"))
.when(0).then(Arel.sql("'Align with first'"))
.else(
Arel::Nodes::InfixOperation.new('||'
Arel::Nodes::NamedFunction.new('TRIM',[
Arel::Nodes::NamedFunction.new('to_char',
[customer_table[:b_cycle_type],Arel.sql("'99th'")])
]),
Arel.sql("' Day Of The Month'")
)
)
Customer.where(case_stmt.eq(your_variable))
SELECT "customers".*
FROM "customers"
WHERE (CASE b_cycle_type
WHEN -1 THEN 'Last day of the month'
WHEN 0 THEN 'Align with first'
ELSE
CASE
WHEN "customers".b_cycle_type BETWEEN -1 AND 31
THEN trim(to_char("customers".b_cycle_type,'99th')||' Day Of The Month')
END
END = '27th Day Of The Month');
But:
If b_cycle_type column is a date, you should define the column as a date type, not a numerical type. It will enable you to do simply where extract('day' from b_cycle_type) = 27. It'll also take care of validating all data anyone tries to insert into the table, without having to maintain custom triggers and constraints.
If for whatever reason you have to have this as a day offset, you should make it a smallint or even decimal(2,0). Also, save the actual date it corresponds to, as a separate column, in order to be able to easily account for months with different lengths, as well as leap years when February is longer.
If you can't alter the "customers" table structure, whenever you deal with the data from the table, you should make sure b_cycle_type is between -1 and 31, and within possible number of days for a given month in a given year.
Given a table like so:
Log:
id, user_id, points, created_at
With Rails, how can I query the database by user_id and then GROUPED by week via created_at where I can then having something like:
WEEK of X/XX/XXXX - 3011 total points
WEEK of X/XX/XXXX - 320 total points
WEEK of X/XX/XXXX - 31 total points
WEEK of X/XX/XXXX - 30330 total points
Thanks
points_by_week = Log.where(user_id: user_id).group("DATE_TRUNC('year', created_at)", "DATE_TRUNC('week', created_at)").sum(:points)
Will yield a result like
{[2014, 4]=>3,
[2014, 8]=>7,
[2015, 4]=>26,
[2015, 6]=>19,
[2015, 12]=>50,
[2015, 32]=>48,
[2016, 2]=>78,
[2016, 3]=>45,
[2016, 4]=>49,
[2016, 5]=>110,
[2016, 45]=>30,
[2017, 4]=>130,
[2017, 11]=>185}
where the key is [year, week], then you can use Date.commercial to get the week of
points_by_week.each do |(year, week), count|
date = Date.commercial(year, week)
puts "Week of #{date.strftime('%d/%m/%Y')} - #{count} total points"
end
And (just cause I can), the mysql version of the query looks like this
points_by_week = Log.where(user_id: user_id).group("year(created_at)", "week(created_at)").sum(:points)
This by default assumes the week starts on Monday, and the first week of a year is starts on the first Monday of the year
something like:
points_by_week = Log.select("DATE_TRUNC('week', created_at) as week, sum(points) as total_points").group("week").where(user_id: x)
points_by_week.each do |pw|
puts "Week of #{pw.week.strftime('%d/%m/%Y')} - #{pw.total_points} total points"
end
EDIT
as per Michael Gorman comment the above won't distinguish between years (2017, 2016, etc), so you might need to group by week and year depending on your requirements:
Log.select("DATE_TRUNC('week', created_at) as week, DATE_TRUNC('year', created_at) as year, sum(points) as total_points").group("week, year")
and you can keep using the above loop to display it ;)
Something like this:
select 'WEEK of ' || TO_CHAR(date_trunc( 'week', created_at ), 'dd/mm/yyyy') || ' - ' || sum(points) || ' total points'
FROM clients
WHERE id < 100000
GROUP BY date_trunc( 'week', created_at );
|| is used to concatenate string.
TO_CHAR is used as a data type formatting functions.
I can't seem to get a date search working properly for my two dates.
I am searching on the created_at date with the following in the searches model.
# -------------------------- Begin created at dates ----------------------------
# Only one date is filled in:
documents = documents.where("documents.created_at >= ?", from_date) if from_date.present? and not to_date.present?
documents = documents.where("documents.created_at <= ?", to_date - 1.day) if to_date.present? and not from_date.present?
# Both Dates are filled in
documents = documents.where("documents.created_at >= ?", from_date,) if from_date.present? and to_date.present?
documents = documents.where("documents.created_at <= ?", to_date) if to_date.present? and from_date.present?
Note that I tried a couple of variations of to_date - 1.day such as to_date and to_date + 1.day
I have a form with fields of from_date and to_date
If I enter just the from date e.g. 03/24/15, I get all of the items I would expect such as
2016-03-24
id Subject Category Author Date Created Date Updated
32 Test Of Dates Test Christopher Mendla 03/24/16 16:45 03/24/16 16:45 Edit
33 Friday Test Christopher Mendla 03/25/16 09:21 03/25/16 09:21
IOW, it works as expected.
However, if I set the to_date to 03/25/16, I get all of the records EXCEPT those records created on 3/25/16. If I search for a to_date of 03/26/16, then the records with a created_at of 3/25/16 are included.
I added the following to application.rb but note that this problem existed before setting the time zones (Yes, I know going with other than UTC for the data can cause problems but these are internal apps and other tools will be used to report from SQL. )
config.time_zone = 'Eastern Time (US & Canada)'
config.active_record.default_timezone = 'Eastern Time (US & Canada)'
I found that I had to modify the SQL statement to change the stored date/time to a date. The Database is on MS Sql Server.
# -------------------------- Begin created at dates ----------------------------
# Only one date is filled in:
documents = documents.where("documents.created_at >= ?", from_date) if from_date.present? and not to_date.present?
documents = documents.where("cast (documents.created_at as date) <= ?", to_date) if to_date.present? and not from_date.present?
# cast ([created_at] as date) <= '2016-03-25'
# Both Dates are filled in
documents = documents.where("documents.created_at >= ?", from_date,) if from_date.present? and to_date.present?
documents = documents.where("cast (documents.created_at as date) <= ?", to_date) if to_date.present? and from_date.present?
The cast (documents.created_at as date) will compare the dates as dates and ignore the time. Apparently a stored date of 2016-03-25 11:40 is NOT less than or equal to 2016-03-25.
I did not use the cast as date on the from because I believe that 2016-03-25 11:40 IS >= 2016-03-25
date_start = Time.parse('11/08/2015').beginning_of_day
date_end = Time.parse('11/08/2015').end_of_day
created_at_day_tz = "date(created_at AT TIME ZONE \'UTC\'
AT TIME ZONE \'#{Time.zone.tzinfo.identifier}\')"
users = User.where("users.created_at BETWEEN ? AND ?", date_start, date_end)
Grouping by created_at as created_at_day (date only, new name for the groupped attribute)
grouped_with_timezone_day = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at_day, count(*) as count")
# grouped_with_timezone_day.map {|u| [u.created_at_day, u.count] }
# => [[Tue, 11 Aug 2015, 186]]
Grouping by created_at as created_at (date only, same name for the groupped attribute)
grouped_with_timezone = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at, count(*) as count")
# grouped_with_timezone.map {|u| [u.created_at, u.count] }
# => [[Mon, 10 Aug 2015 21:00:00 BRT -03:00, 186]]
Why the results differ if the records are the same? Why one result comes with timezone, as DateTime, and the other comes as Date only?
Is activerecord 'casting' to DateTime with Timezone because created_at is defined that way (btw, this makes the dates incorrect in this case)?
The timestamp isn't incorrect - that is, it's 2015-08-11 at midnight UTC - it's just displaying in your local time.
Rails has a bit of special behavior for created_at and updated_at:
The timestamps macro adds two columns, created_at and updated_at. These special columns are automatically managed by Active Record if they exist.
It always treats created_at coming back from a query as a timestamp. Your query returns just the date 2015-08-11, which is interpreted as midnight. When printed, the timestamp is displayed in your locale's timezone (which I presume must be -03:00), leading to 3 hours before midnight on the 11th.
When you name the result created_at_day, you avoid Rails converting it to a timestamp and get just the date you expect.
#events = Event.all(:order => "date DESC")
to order my events by date, a datetime column.
Is it possible to order them not by date, but only by hour? (I'm suing sqlite3 database)
Thanks!
For SQLite,
#events = Event.all(:order => "time(date) DESC")
Use that with care, because it ignores the date. (And see "Moments later" below.)
CREATE TABLE test (date datetime primary key);
INSERT INTO "test" VALUES('2011-01-01 08:00:00');
INSERT INTO "test" VALUES('2011-01-01 08:13:00');
INSERT INTO "test" VALUES('2011-01-01 09:23:00');
INSERT INTO "test" VALUES('2011-01-02 09:15:00');
Only one of the dates is on Jan 2.
sqlite> select * from test order by time(date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:13:00
2011-01-01 08:00:00
Moments later . . .
I realized you wanted to sort by hour, not by time. That questionable requirement takes a different expression, and sorts differently.
#events = Event.all(:order => "strftime('%H', date) DESC")
sqlite> select date from test order by strftime('%H', date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:00:00
2011-01-01 08:13:00
The last two rows are sorted correctly by hour, incorrectly by time.
Still later . . .
The OP deploys on Heroku, which doesn't support SQLite. To sort by hour descending, the OP probably needs something like
#events = Event.all(:order => "extract (hour from date) DESC")
And stop using one platform for development and a different platform for deployment.
Since Heroku uses PostgreSQL, you can use:
#events = Event.all(:order => "date_part(hour, date) DESC")
You'll have problems developing if you use SQLite3 locally and PostgreSQL for deployment - SQL is not consistent between platforms.