Using case to select field based on value of another field - sql

i have a database three fields: booking type, booking num and booking date. Some values are
Bookingtype Bookingnum date
main 0001 10-11-15
main 0002 09-11-15
sub 0002a 12-12-15
i need to write a select statement for the date for each record. But when the booking type is sub it should get the date from the main booking date. in this case booking 2a should have date 09-11-5..I'm using postgres 9.3 and have simplified the fields to make it easier for others to understand what i need.

Assuming your main numbers are always the first 4 characters of Bookingnum (a clear definition is missing):
SELECT b.Bookingtype, b.Bookingnum
, COALESCE(main.date, b.date) AS date
FROM bookings b
LEFT JOIN bookings main ON b.Bookingtype = 'sub'
AND main.Bookingnum = left(b.Bookingnum, 4);
I would consider a foreign key column (self-reference) to the main row.

Related

I want to make SQL tables that are updated daily yet retain every single day's contents for later lookup. What is the best practice for this?

Basically I'm trying to create a database schema based around multiple unrelated tables that will not need to reference each other AFAIK.
Each table will be a different "category" that will have the same columns in each table - name, date, two int values and then a small string value.
My issue is that each one will need to be "updated" daily, but I want to keep a record of the items for every single day.
What's the best way to go about doing this? Would it be to make the composite key the combination of the date and the name? Or use something called a "trigger"?
Sorry I'm somewhat new to database design, I can be more specific if I need to be.
Yes, you have to create a trigger for each category table
I'm assuming name is PK for each table? If isnt the case, you will need create a PK.
Lets say you have
table categoryA
name, date, int1, int2, string
table categoryB
name, date, int1, int2, string
You will create another table to store changes log.
table category_history
category_table, name, date, int1, int2, string, changeDate
You create two trigger, one for each category table
Where you save what table gerate the update and what time was made.
create trigger before update for categoryA
INSERT INTO category_history VALUES
('categoryA', OLD.name, OLD.date, OLD.int1, Old.int2, OLD.string, NOW());
This is pseudo code, you need write trigger using your rdbms syntaxis, and check how get system date now().
As has already been pointed out, it is poor design to have different identical tables for each category. Better would be a Categories table with one entry for each category and then a Dailies table with the daily information.
create table Categories(
ID smallint not null auto_generated,
Name varchar( 20 ) not null,
..., -- other information about each category
constraint UQ_Category_Name unique( Name ),
constraint PK_Categories( ID )
);
create table Dailies(
CatID smallint not null,
UpdDate date not null,
..., -- Daily values
constraint PK_Dailies( CatID, UpdDate ),
constraint FK_Dailies_Category foreign key( CatID )
references Categories( ID )
);
This way, adding a new category involves inserting a row into the Categories table rather than creating an entirely new table.
If the database has a Date type distinct from a DateTime -- no time data -- then fine. Otherwise, the time part must be removed such as by Oracle's trunc function. This allows only one entry for each category per day.
Retrieving all the values for all the posted dates is easy:
select C.Name as Category, d.UpdDate, d.<daily values>
from Categories C
join Dailies D
on D.CatID = C.ID;
This can be made into a view, DailyHistory. To see the complete history for Category Cat1:
select *
from DailyHistory
where Name = 'Cat1';
To see all the category information as it was updated on a specific date:
select *
from DailyHistory
where UpdDate = date '2014-05-06';
Most queries will probably be interested in the current values -- that is, the last update made (assuming some categories are not updated every day). This is a little more complicated but still very fast if you are worried about performance.
select C.Name as Category, d.UpdDate as "Date", d.<daily values>
from Categories C
join Dailies D
on D.CatID = C.ID
and D.UpdDate =(
select Max( UpdDate )
from Dailies
where CatID = D.CatID );
Of course, if every category is updated every day, the query is simplified:
select C.Name as Category, d.UpdDate as "Date", d.<daily values>
from Categories C
join Dailies D
on D.CatID = C.ID
and D.UpdDate = <today's date>;
This can also be made into a view. To see today's (or the latest) updates for Category Cat1:
select *
from DailyCurrent
where Name = 'Cat1';
Suppose now that updates are not necessarily made every day. The history view would show all the updates that were actually made. So the query shown for all categories as they were on a particular day would actually show only those categories that were actually updated on that day. What if you wanted to show the data that was "current" as of a particular date, even if the actual update was several days before?
That can be provided with a small change to the "current" query (just the last line added):
select C.Name as Category, d.UpdDate as "Date", d.<daily values>
from Categories C
join Dailies D
on D.CatID = C.ID
and D.UpdDate =(
select Max( UpdDate )
from Dailies
where CatID = D.CatID
and UpdDate <= date '2014-05-06' );
Now this shows all categories with the data updated on that date if it exists otherwise the latest update made previous to that date.
As you can see, this is a very flexible design which allows access the data just about any way desired.

Filling one table using another table in SQL Server

I have two SQL tables as follows:
As you may note, the first table has a monthly frequency (date column), while the second table has a quarterly frequency. Here is what I would like to do:
For each issueid from table 1, I would like to look at the date, determine what is the previous end of quarter, and go fetch data from table 2 corresponding to that issue for that end of quarter, and insert it in the first table in the last two columns.
For example: take issueid 123456 and date 1/31/2014. The previous end of quarter is 12/31/2013. I would like to go to table 1, copy q_exp and q_act that correspond to that issueid and 12/31/2013, and paste it into the first table.
Of course, I would like to fill the entire first table and minimize manual inserts.
Any help would be appreciated! Thanks!
Try the following query
UPDATE issues
SET q_exp=(SELECT TOP 1 q.q_exp
FROM quarterlyTable q
WHERE q.issueid=i.issueid
AND q.[date]<=i.[date]
ORDER BY q.[date] DESC)
,q_act= (SELECT TOP 1 q.q_act
FROM quarterlyTable q
WHERE q.issueid=i.issueid
AND q.[date]<=i.[date]
ORDER BY q.[date] DESC)
FROM issues i

Selectively retrieve data from tables when one record in first table is linked to multiple records in second table

I have 2 tables:
1. Tbl_Master: columns:
a. SEQ_id
b. M_Email_id
c. M_location_id
d. Del_flag
2. Tbl_User: columns
a. U_email_id
b. Last_logged_date
c. User_id
First table Is master table it has unique rows i.e. single record of all users in the system.
Each User can be uniquely identified by the email_id in each table.
One user can have multiple profile, which means for one us_email_id field in the tblUser table, there can be many user_id in tbl_User,
i.e there can be multiple entries in second table for each user.
Now I have to select only those users who have logged in for last time before, lets say '2012', i.e before 1-Jan-2012.
But if one user has 2 or more user_id and one user_id has last_logged_date less than 2012
But other user_id has greater than 2012 then such user should be ignored.
In the last all all the result user will be marked for deletion by setting DEL_flag in master table to ‘Yes’
For eg:
Record in Tbl_Master:
A123 ram#abc.com D234 No
A123 john#abc.com D256 No
Record in tbl_User can be Like:
ram#abc.com '11-Dec-2011' Ram1
ram#abc.com '05-Apr-2014' Ram2
john#abc.com '15-Dec-2010' John1
In such case only John's Record should be selected not of Ram whose one profile has last_logged_date>1-Jan-2012
Another possibility was
SELECT
m.M_Email_id,
MAX(u.Last_logged_date) AS last_login
FROM
Tbl_Master m
INNER JOIN
Tbl_User u on u.U_email_id = m.M_Email_id
GROUP BY m.M_Email_id
HAVING
-- Year(MAX(u.Last_logged_date)) < 2012 -- use the appropriate function of your DBMS
EXTRACT(YEAR FROM(MAX(u.Last_logged_date))) < 2012 -- should be the version for oracle
-- see http://docs.oracle.com/cd/B14117_01/server.101/b10759/functions045.htm#i1017161
Your UPDATE operation can use this select in the WHERE clause.
Try this, this ans is in sql server, I haven't worked on Oracle.
select * from Tbl_Master
outer apply
(
select U_email_id,max(Last_logged_date)as LLogged,count(U_email_id) as RecCount
from Tbl_User
where Tbl_User.U_email_id = Tbl_Master.M_Email_id
group by U_email_id
)as a
where RecCount >2
and Year(LLogged) < '2012'
Try this DEMO
Hope it helps you.

How can I make multiple records to be printed as a single row

alt text http://img59.imageshack.us/img59/962/62737835.jpg
This three columns are taken from 3 tables. In other words, these records are
retrieved by joining 3 tables.
It is basically a very simple time sheet that keeps track of shift starts time, lunch time and so on.
I want these four records to show in one row, for example:
setDate --- ShiftStarted --- LunchStarted --- LunchEnded ---- ShiftEnded ----- TimeEntered
Note: discard TimeEntered column. I will deal with this later, once i know how to solve the above issue, it will be easy for me to handle the rest.
How can i do it?
Further Info - Here is my query:
SELECT TimeSheet.setDate, TimeSheetType.tsTypeTitle
FROM TimeSheet
INNER JOIN TimeSheetDetail ON TimeSheet.timeSheetID = TimeSheetDetail.timeSheetID
INNER JOIN TimeSheetType ON TimeSheetType.timeSheetTypeID = TimeSheetDetail.timeSheetTypeID
TimeSheet table consists of the following columns:
timeSheetID
employeeID - FK
setDate
setDate represents today's date.
TimeSheetType table consists of the following columns:
timeSheetTypeID
tsTypeTitle
tsTypeTitle represents shifts e.g. shift starts at, lunch starts at, shift ends at, etc.
TimeSheetDetail table consists of the following columns:
timeSheetDetailID
timeSheetID - FK
timeSheetTypeID - FK
timeEntered
addedOn
timeEnetered represents the time that employee set manually.
addedOn represents the system time, the time that a record was inserted.
I must admit I haven't fully read all but I think you can work out the rest for yourself. Basically you can join the table timesheet with itself.
I did this ...
create table timesheet (timesheet number, setdate timestamp, timesheettype varchar2(200), timeentered timestamp);
insert into timesheet values (1,to_date('2010-08-02','YYYY-MM-DD'),'Shift Started',current_timestamp);
insert into timesheet values (1,to_date('2010-08-02','YYYY-MM-DD'),'Lunch Started',current_timestamp);
insert into timesheet values (1,to_date('2010-08-02','YYYY-MM-DD'),'Lunch Ended',current_timestamp);
insert into timesheet values (1,to_date('2010-08-02','YYYY-MM-DD'),'Shift Ended',current_timestamp);
commit;
select * from timesheet t1
left join timesheet t2 on (t1.timesheet = t2.timesheet)
where t1.timesheettype = 'Shift Started'
and t2.timesheettype = 'Lunch Started'
... and got out this
TIMESHEET SETDATE TIMESHEETTYPE TIMEENTERED TIMESHEET_1 SETDATE_1 TIMESHEETTYPE_1 TIMEENTERED_1
1 02.08.2010 00:00:00.000000 Shift Started 05.08.2010 12:35:56.264075 1 02.08.2010 00:00:00.000000 Lunch Started 05.08.2010 12:35:56.287357
It was not SQL Server but in principle it should work for you too.
Let me know if you still have a question
You might want to check out the PIVOT operator. It basically allows you to use particular row values to create new columns in your result set.
You'll have to supply an aggregate function for combining multiple rows - for instance (assuming you split your data on a per day basis), you'll have to decide how to deal with multiple "shift started" events on the same day. Assuming that such events never occur, you'll still have to use an aggregate. MAX() is usually a safe choice in those circumstances.

SQL Query for range of dates

Lets frame the question again---
table1{date, bID, sName, fID}
{11/05,B1,A1,P1}
{12/05,B2,A2,P2}
{13/05,B1,A3,P1}
{15/05,B3,A4,P1}
{16/05,B1,A5,P2}
{19/05,B1,A6,P2}
This is the table and the data stored in the table is also specified...
Now the query that i want is that:-
Depending of fId (lets say, P1 is selected) it should display the data from table say from 11/05-17/05 (no date should be missed). The data retrieved is as follows:-
11/05,B1,A1
12/05,--,--
13/05,B1,A3
14/05,--,--
15/05,B3,A4
16/05,--,--
17/05,--,--
The data retrieved for a particular fID(say P1) is displayed.. Explaning the result...
1) it displayed all data from 11/05-17/05 where fId is P1, if there is no date in the database, then also it should display null value for that date (i.e.14/05 date was not there in database, but still it displayed with a null value)..
2) if fId for that particular date is not P1, then also it store a null value in result set..
Atlast the data is retrieved in result set,, and processed further..
So i want to write the query for this problemm,, is it possible..
No code here, just my thoughts.
You need to create a temporary table with dates ranging from your begin date to an end date, inclusive. And then left join table1 with that temporary table on date column plus add where fID = ?.
As the other answer here mentions, a table with all the dates in it, and a LEFT JOIN is what you need.
Say you have this table:
CREATE TABLE table1
{
date DATETIME
bID VARCHAR(10),
sName VARCHAR(10),
fID VARCHAR(10)
}
and then this date-table:
CREATE TABLE dates
(
dt DATETIME
)
and in this table you need to have all the dates for the range you want to display. Usually you populate it with a couple of years in both directions, but that's up to you.
Note: For simplicity, I did not bother with primary keys in either table. You should of course make sure you have a primary key, and in the case of the dates table, it could be the dt column.
Then to display the results you want:
SELECT
dt,
bID,
sName
FROM
dates
LEFT JOIN table1 ON dt = date AND fld = 'P1'
ORDER BY
dt
Note that the selection of only P1 rows is done in the JOIN criteria. If you add a WHERE clause to do the same, you'll loose all dates that have no data.