Which SQL would work? - sql

I am trying to work out the best DB structure and SQL for my application.
The scenario is this:
Jim and John need to update their time sheets each week for different
projects. When they do this they use an html Form to insert their
hours based a date of the last day of the week.
This is fine and will insert a new row with NAME, Project, Date and Hours
However if jim and john do more hours than they expected and need to update their time sheet, I want them to be able to use the form and it will UPDATEthe hours coloumn on the DB if the name, project name and date are the matching the what has been submited.
SQL TO INSERT:
INSERT OR REPLACE INTO RESOURCE(NAME, DATE, PROJECT, HOURS) VALUES ('"+ name+ "', '"+ date+ "', '"+ project+ "','" + hours + "');
Work so far:
I have create the below DB structure on an SQLITE DB.
CREATE TABLE RESOURCE (
name VARCHAR(16),
date DATE,
project VARCHAR(16),
hours VARCHAR(16)
)
And I have create the following index to attempt to solve my issue.
CREATE UNIQUE INDEX myindex ON RESOURCE(NAME, DATE, PROJECT)
However, the problem is when jim updates his hours for the same date, project and hours as john, Johns row is replaced and only jims is left.
Hopefully I have explained this issue clearly, if not please let me know and I will supply whatever else is needed.
My question is: What can I change with either my SQL or my DB structure that would allow this aforementioned scenario to work?

What i got is that when jim/jhon enters his timing again an update query executes which update the whole records instead of updating jim/jhons own record. the main reason is that you don't have any unique key/primary key on your table that's why your query updates whole lot.
what you need to do is that have a key column and on the basis of that key you will update their records you will be using something like this in where clause
WHERE RESOURCE_ID = #RID //ID OF THAT PARTICULAR RESOURCE

Related

I have a table where I need to update or insert depending on field paramaters

I have spent many hours researching this problem and trying various solutions but I never quite find a suitable solution for my specific problem. I am new to SQL and some of the examples are confusing as well.
So here is my dilemma. I have a equipment table that tracks oil changes for specific units in a database. The table looks like this:
**id UnitID Posted_On Date_Completed Note OverDueBy**
1 BT-109F 2019-02-04 2019-02-14 Hrs Overdue 23
1 BT-108G 2020-01-17 2020-01-22 Days Overdue 12
1 BT-122K 2020-01-02 2020-01-16 Days Overdue 12
1 BT-109F 2019-02-04 Days Overdue 3
The example records above need to be created or updated by the query. The date completed is entered manually by the technician when he has completed the oil change.
What I want the query to do is, Check to see if a specific Unit has a record where the 'Date_Completed' field is empty, and if so update the 'OverDueBy' field to reflect the new value. If all the records for the specified Unit have the 'Date_Completed' fields filled in, then the query should create a new record will all fields filled in except for the 'Date_Completed' field.
Can anyone help me construct such a query?
Thanks
Clan
First create a unique partial index for the column UnitID:
CREATE UNIQUE INDEX idx_unit ON tablename(UnitID)
WHERE Date_Completed IS NULL;
so that only 1 row with Date_Completed=null is allowed for each UnitID.
So a statement like this:
INSERT INTO tablename(id, UnitID, Posted_On, Date_Completed, Note, OverDueBy)
VALUES (?, 'BT-109F', ?, null, ?, ?)
ON CONFLICT(UnitID) WHERE Date_Completed IS NULL DO UPDATE
SET OverDueBy = ?;
will insert the new values only if there is no row already for UnitID='BT-109F' with null in Date_Completed.
But if there is such a row then it will update the column OverDueBy.
I'm not sure what values you want to insert or what will be the updated value so replace the ? with the appropriate values.
Firstly I would use a view rather than a table to store any calculated data - it reduces storage overheads and will update the calculation every time the view is opened.
If you're using SQLite you should be able to get the overdue by subtracting the Posted_On from its function to return today's date something like date('now') or julianday('now') - read up on and test the functions to ensure it does what you want.
So along the lines of:-
create view MyView as select *, julianday('now') - julianday(Posted_On) as OverDueBy from ClansTable where Date_Completed is null;
If you want to store a snapshot you can always create a table from a view in any case:-
create table MyStoredOverduesOn4thFeb as select * from MyView;
You can find your units that have all Date_Completed and create a single new record like so:-
Create table CompletedUnits as select id, UnitID, max(posted_on) as latest_posted_on, '' as Date_Completed from ClansTable group by id, UnitID having count(*) = count(Date_Complete);
Test this SQL and see if you can get it working - note I've created a text field for the date. Apparently there is no date/datetime data type as such:-
https://www.sqlitetutorial.net/sqlite-date/
Hope this helps,
Phil
I think you need something like this:
MERGE INTO EQUIPMENT A
USING (SELECT * FROM EQUIPMENT B WHERE DATE_COMPLETED IS NULL) C
ON (A.UNITID=C.UNITID)
WHEN MATCHED THEN UPDATE SET A.OVERDUEBY="new value"
WHEN NOT MATCHED THEN INSERT (A.id,A.UnitID,A.Posted_On,A.Date_Completed,A.Note,A.OverDueBy)
VALUES (C.id,C.UnitID,C.Posted_On,NULL,C.Note,C.OverDueBy)
Not sure where new values from update will come from. It's not clear in your question. But something like this could work.

ERROR HANDLING and Loading into target Table using SQL

I have a scenario Today where I am stucked Up little bit. I have data like this below in my Source Oracle Source Table(Sample Data)
ID,NAME,SALARY,BIRTHDAY
1,ABHIJIT,2000,17/12/1990
2,ROHIT,-2000,13/11/1988
3,MOHIT,500,2075-575-43
Now Salary in the 2nd Row is negetive and BIRTHDAY in the 3rd Row is an Invalid Format(Valid is MM/dd/yyyy). Both 2nd and 3rd Row should go to INVALID_EMPLOYEE and 1st record should go to VALID_EMPLOYEE. In the Source File Date Format is coming as dd/mm/yyyy. Which I have to convert to MM/dd/yyyy format and also have to check whether Date Format incoming in Source File is dd/mm/yyyy or not. Salary should not be less than 0. Source Table All columns are in String and in Target Table ID is Integer,NAME as VARCHAR2(255), SALARY is NUMBER and BIRTHDAY is DATE.
I have handled all this in my Project ETL Tool. So I am trying to push all this in Query to improve performance.Any help is much appreciated
Although the error records should be ideally handled by the process that's doing the insert, in many cases this may not be possible to do.
Instead what you can do now is to set up constraints on the table for the Salary column. The date checking can be implemented by simply having the column of the date datatype. Then create a error logging table to log the errors.
Now we need a table to log your invalid records. For this you have two options.
You can have the INVALID_EMPLOYEES table itself as the error table. But that would mean having additional columns in the table (ORA_ERR_MESG$ and ORA_ERR_TAG$).
If having additional columns is not an option then you can create a dedicated error table and then move the records from this table to your INVALID_EMPLOYEES table. You will have to write some code that runs after your insert code to move the records.
See the DBMS_ERRLOG package to see how to create the error logging tables.
Once you have your error logging tables created you have to instruct your INSERT code to log the errors into your error logging table. This will ensure that all the valid records get inserted in your employees table and the invalid records go to your error table with a column that stores the error that was encountered while inserting that particular record. So your insert code would look something like
INSERT INTO VALID_EMPLOYEES
SELECT ID,
NAME,
SALARY,
BIRTHDAY
FROM my_source_table
LOG ERRORS INTO INVALID_EMPLOYEES ('Oops this guy failed');
In this case 'Oops this guy failed' would go into the ORA_ERR_TAG$ column of the error table. You can use this tag column to identify different insert statements that tried to insert an invalid record.
Hope that helps!

Attempting to add a new column in SQL by subtracting a year

Noob at this. So I will cut to the point on this. This is a class assignment and my professor has not answered emails since Thursday. The problem I am trying to solve is -
"Write a query that will subtract one year from the production date of each album and label this as the RecordDate and display the album title, production date, and record date for each entry in the table. Order the results by album title."
Here is what the table is supposed to look like
Here is the query I used (given in class I know repeated inserts but what he wanted)...
CREATE TABLE ALBUM(
ALBUM_ID char(4) UNIQUE NOT NULL,
ALBUM_TITLE varchar(255),
ALBUM_YEAR year,
ALBUM_PRODUCER varchar(255),
Primary Key(ALBUM_ID)
);
INSERT INTO ALBUM (ALBUM_ID, ALBUM_TITLE, ALBUM_YEAR, ALBUM_PRODUCER)
VALUES ('A001', 'Awake', '1994', 'East West Record');
INSERT INTO ALBUM (ALBUM_ID, ALBUM_TITLE, ALBUM_YEAR, ALBUM_PRODUCER)
VALUES ('A002', 'Moving Pictures', '1981', 'Anthem');
INSERT INTO ALBUM (ALBUM_ID, ALBUM_TITLE, ALBUM_YEAR, ALBUM_PRODUCER)
VALUES ('A003', 'Damage', '2013', 'RCA REcords');
INSERT INTO ALBUM (ALBUM_ID, ALBUM_TITLE, ALBUM_YEAR, ALBUM_PRODUCER)
VALUES ('A004', 'Continuum', '2006', 'Columbia Records');
Here is what I used to START to answer the question
ALTER TABLE ALBUM ADD RECORD_DATE INT;
UPDATE ALBUM SET RECORD_DATE=(ALBUM_YEAR-1);
This does make a new column and gives the results for what I want so far (have not gotten to the later part of the question). But this is two different queries...
So, from advice from SQL experts, to achieve what he wants will I have to write multiple queries or can this be done in one single query? Also are my datatypes okay?
No, I am not asking for the SQL to do this. This isn't a "PLEASE DO MY HOMEWORK FOR ME". And sorry, but it has to all be in cap locks because he wants it that way.
Andrew the tutor hasn't asked you to add a new column or anything, he has asked you to present data in a specific way.
We store data in SQL Server using some specific rules called Database Normalization rules.
But to show data in any specific form we write SELECT queries which select the data from the tables and we use all sorts of functions and methods to manipulate data at run-time and present the data in required structure/format/way.
Similarly in this case for your requirement you do not need to add another column just to do what your tutor as asked to do, it actually violates the rules of normalization. All you need is a simple SELECT query, which will show the data in the required format/way.
The select query will be something as simple as ....
SELECT [ALBUM_TITLE] AS [Album Title]
,[ALBUM_YEAR] AS [Production Date]
,[ALBUM_YEAR] - 1 AS [Record Date]
FROM ALBUM
ORDER BY [ALBUM_TITLE]
You don't need to alter the table at all. You're just going to want to select an additional column. I won't write the full query for you, but I will show you what I mean:
SELECT ALBUM_TITLE, ALBUM_YEAR, ALBUM_YEAR - 1 AS RECORD_DATE
-- rest of the query here --

How can I auto-generate complex identifiers and ensure uniqueness?

I've been handed a very specific business requirement, and I'm having trouble translating it to work within our database.
We have multiple departments, who all deal with what we call files. Each file has a unique identifier, but how this identifier is assigned depends on the department that the file is associated with.
Departments ABC, DEF and GHI need the system to provide them with the next identifier following a pattern... while departments JKL and MNO need to be able to specify their own identifiers.
This is further compounded by the fact that the generated file numbers are semi complex. Their file numbers follow the pattern:
[a-z]{3,4}\d{2}-\d{4}
(A 3 or 4 letter prefix that corresponds to the department with two digits corresponding to the year, a dash followed by a four digit generated number - i.e. ABC13-0001)
The part before the dash is easy to generate... the file table has a mandatory foreign key reference to a department table that has the prefix column, and I can grab the year just as easily. it's the part after the dash that I can't seem to work out.
It's a four digit identifier. Each department needs the next generated ID to be as sequential to their department as possible (I say as sequential as possible, as I'm aware that even identity specifications can leave gaps). On top of that, we need to reset it back to 0001 each year. All of that, while ensuring that there are no duplicates, which is the most important part of all of this.
So, we only have one file table that is used by all of these departments. As such, to be able to handle JKL and MNO, I have the FileNumber field set to varchar(12) with a unique constraint. They can type in whatever they need, so long as it's unique. That just leaves me with how to generate the unique file numbers for the other departments.
My first instinct is to give the file table a surrogate identity primary key to make sure that, even if something goes wrong with the generated file number, that each record is guaranteed a unique identifier.
Then I would create a single row table that has two columns per department, one for a number and one for a date. The number would be the last used number for the 4 digit identifier suffix for a given department (as an int), and the date would be when it was assigned. Storing the date would let me check if the year has changed since the last id was pulled so that I can assign 1, instead of lastid + 1.
Then an insert trigger on the file table would generate the file id using:
The foreign key to the department table to get the department prefix
CONVERT(VARCHAR, YEAR( GETDATE() ) % 100) to pull the current two digit year
A select to the above described utility table to get the last id + 1 (or reseting to 1 if the current year differs from the year of the last updated date).
The trigger would finally update the utility table with the last used id and date.
In theory, I think that would work... and the unique constraint on the file id column would prevent an insert where the generated file id already exists. But it feels so brittle, and I can foresee that unique constraint being a double edged sword that would possibly prevent a department from creating a new file should the trigger fail to update the utility table. After all, if it doesn't, then the next time a file number is generated, it would try to use the same generated id, and fail. There must be a better way.
My other thought was to have a table per department with just an identity integer column and non-null date field with a default of (getdate())... and have the trigger on the file table insert a new row, and use that id. It would also be responsible for deleting all rows in the given department id table and reset the identity come the new year. It feels more secure to me, but then I have 5 utility tables (1 per department that auto-generate ids) with up to 9999 records, just to generate an id.
Am I missing something simple? Am I going about this the right way? I just can't help but think that there must be an easier way. Am I right to try to make the SQL server responsible for this, or should I try doing this in the desktop application that I will build on top of this database?
So, I think it's possible you're overcomplicating this. It's also possible I'm misunderstanding the requirements. I don't believe you need separate tables or anything, you just need to check the existing IDs and increment them. SQLFiddle doesn't seem to be working right now, but here's a method I came up with in my local DB:
create table dept_ids (
id varchar(12) primary key
)
insert into dept_ids values('ABC13-0001')
insert into dept_ids values('DEF13-0001')
insert into dept_ids values('GHI13-0001')
insert into dept_ids values('JKL13-0001')
insert into dept_ids values('ABC13-0002')
declare
#dept varchar(4)
, #year varchar(2)
, #prefix varchar(8)
, #new_seq int
set #dept = 'ABC'
select #year = right(cast(DATEPART(yy, getdate()) as varchar(4)), 2)
set #prefix = #dept + #year + '-'
select #new_seq = isnull(right(max(id), 4), 0) + 1 from dept_ids where id like #prefix + '%'
select new_id = #prefix + right(replicate('0', 4) + cast(#new_seq as varchar(4)), 4)
This handles the different departments of course, as well as the year scenario by using getdate() and an ISNULL check. For the departments that can enter their own sequence values, you can just add a null check for that parameter and skip generating this value if it's present.
If I'm oversimplifying, feel free to let me know and I'll adjust.

using UPDATE, SELECTand INSERT INTO statements altogther in MS Access

I have an MS access project in which the tabel of transactions contains as many as 70 records per day. they are common in one parameter, the Date of today.
the normal way to start entering data is to go to teh table and copy records of yesterday then paste them as new records, then changing date from yesterday to today date.
What I need is to use SQL statements to achieve this. I can say that
step 1: use insert into to input the new records in the table.
Step 2: use update statement to change the date.
Step 3: use the select statement to select the records which will have the date to be changed.
How to combine the use of these statements altogther.
thanks
You need something on the lines of:
INSERT INTO Table (Field, Field, Field, ADate)
SELECT Field,Field,Field,Date() As ADate
FROM Table WHERE ADate = Date()-1
Or just change the default value of the date field in the table to Date(), or Now(), and only insert the other fields. Date will then be automatically filled in as today.