Design of Table when it comes to multiple dates for selection - database

I have a table Hall which has structure like
CREATE TABLE Hall_tbl (
hall_id INTeger PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT,
price INTEGER,
capacity INTEGER,
availibility INTEGER
);
Now, intention of the application is to make it work in a way that User looks at the calendar -> Selects a date -> If Hall is available on that particular day then User can interact with it else not.
So, it boils down to the point that Availibility is important and it should be reference to bunch of Dates. Once user open a particular date, he is presented with Time Slots. Depending on the Available Time Slots user can select the Hall. I am having hard time in getting idea how would it be possible to implement that.
First I thought to create a table called dates with columns date starting from 1-1-2018 to 1-1-2020 and create multiple time slot like 0-1 1-2 .. 2-24 and putting 0 to indicate they are not sold and I will change to 1 when User select an open Hall. But this will take me to create 365*4 rows and 25 columns which is not at all efficient. So how can I do this ?
Many application related to Ticket Booking, Flight booking have already implemented such stuff. So, How do they do it efficiently ??
Technology I am using is SQLITE

What you could do is have a table for the opening dates and add just the opening dates and then check if there is a row for the date if so then the hall is open otherwise it is not.
So based upon your Hall_tbl your could have :-
Your Hall_tbl
CREATE TABLE IF NOT EXISTS Hall_tbl (
hall_id INTEGER PRIMARY KEY,
name TEXT,
price INTEGER,
capacity INTEGER,
availibility INTEGER
);
AUTOINCREMENT isn't required and it is recommended to not use it unless necessary SQLite Autoincrement
availability column isn't required but has been left
Add some halls (4 id's 1-4)
INSERT INTO Hall_tbl (name, price, capacity, availibility)
VALUES('Grand Hall',200,50,0),
('Large Hall',100,25,0),
('Normal Hall',75,20,0),
('Small Hall',50,15,0);
The Hall opening times table
CREATE TABLE IF NOT EXISTS hall_opening_times (hall_reference, open_date);
Add some opening times (5)
INSERT INTO hall_opening_times
VALUES (1,'2018-04-23'), -- Grand hall open on 2018-04-23
(2,'2018-04-23'), -- Large Hall open on 2018-04-23
(2,'2018-04-25'), -- Large Hall open on 2018-04-25
(3,'2018-04-24'), -- Normal Hall open on 2018-04-24
(4,'2018-04-23'); -- Small Hall open on 2018-04-23
Checking if a hall is open
The following would return a single row with a single column where the value would be 0 (not open) or 1 (open) :-
This checks the Grand Hall for today (it's 2018-04-23 here)
SELECT count() > 0 AS hall_open FROM hall_opening_times WHERE hall_reference = 1 AND date(open_date) = date('now');
it returns 1 (in column hall_open) as the Grand Hall (ID = 1) is open on 2018-04-03 today (date('now'))
This one checks the Normal Hall for today
SELECT count() > 0 AS hall_open FROM hall_opening_times WHERE hall_reference = 3 AND date(open_date) = date('now');
it returns 0 (in column hall_open) as there are no entries for ID 3 on 2018-04-03
A little more
You could get a list of Open Halls using the following :-
SELECT group_concat(name,' - ') AS open_halls
FROM hall_opening_times JOIN Hall_tbl ON hall_reference = Hall_tbl.hall_id
WHERE open_date = date('now')
Which on 2018-04-23 would return (in column open_halls):-
Grand Hall - Large Hall - Small Hall
If the previous SQL were change to (to check tomorrow 2018-04-24) :-
SELECT group_concat(name,' - ') AS open_halls
FROM hall_opening_times JOIN Hall_tbl ON hall_reference = Hall_tbl.hall_id
WHERE open_date = date('now','+1 DAY')
Then it would return :-
Normal Hall
You could prune past open_times (if wanted) using something like :-
DELETE FROM hall_opening_times WHERE open_date < date('now','-1 WEEK');
will delete rows older than 1 week from the current date.
Note regarding date formats
The date format yyyy-MM-dd is one recognised/supported by SQLite date functions using that rather than d-M-yyyy can make things significantly simpler.
See - SQL As Understood By SQLite - Date And Time Functions

Related

Filtering SQL rows based on certain alphabets combination

I have a column that store user input text field from a frontend website. User can input any kind of text in it, but they will also put in a specific alphabets combination to represent a job type - for example 'dri'. As an example:
Row 1: P49384; Open vehicle bonnet-BO-dri 22/10
Row 2: P93818; Vehicle exhaust-BO 10/20
Row 3: P1933; battery dri-pu-103/2
Row 4: P3193; screwdriver-pu 423
Row 5: X939; seats bo
Row 6: P9381-vehicle-pu-bo dri
In this case, I will like to filter only rows that contain dri. From the example, you can see the text can be in any order (user behaviour, they will key whatever they like without following any kind of format). But the constant is that for a particular job type, they will put in dri.
I know that I can simply use LIKE in SQL Server to get these rows. Unfortunately, row 4 is included inside when I use this operator. This is because screwdriver contains dri.
Is there any way in SQL Server I can do to strictly only obtain rows that has dri job type, while excluding words like screwdriver?
I tried to use PATINDEX but it failed too - PATINDEX('%[d][r][i]%', column) > 0
Thanks in advance.
Your data is the problem here. Unfortunately even for denormalised data it doesn't appear to have a reliable/defined format, making parsing your data in a language like T-SQL next to impossible. What problems are there? Based on the original sample data, at a glance the following problems exist:
The first data value's delimiter isn't consistent. Rows 1-5 use a semicolon (;), but row 6 uses a hyphen (-)
The last data value's delimiter isn't consistent. Row 1, 2 & 4 use a space ( ), but row 3 uses a hyphen (-).
Internal data doesn't use a consistent delimiter. For example:
Row 1 has a the value Open vehicle bonnet-BO-dri, which appears to be the values Open vehicle bonnet, BO and dri; so the hyphen(-) is the delimiter.
Row 5 has seats bo, which appears to be the values seats and bo, so uses a space ( ) as a delimiter.
The fact that row 6 has vehicle as its own value (vehicle-pu-bo-dri), however, implies that Open vehicle bonnet and Vehicle Exhaust (on rows 1 and 2 respectively) could actually be the values Open, vehicle, & bonnet and Vehicle & Exhaust respectively.
Honestly, the solution is to fix your design. As such, your tables should likely look something like this:
CREATE TABLE dbo.Job (JobID varchar(6) CONSTRAINT PK_JobID PRIMARY KEY NONCLUSTERED, --NONCLUSTERED Because it's not always ascending
YourNumericalLikeValue varchar(5) NULL); --Obviously use a better name
CREATE TABLE dbo.JobTypeCompleted(JobTypeID int IDENTITY (1,1) CONSTRAINT PK_JobTypeID PRIMARY KEY CLUSTERED,
JobID varchar(6) NOT NULL CONSTRAINT FK_JobType_Job FOREIGN KEY REFERENCES dbo.Job (JobID),
JobType varchar(30) NOT NULL); --Must likely this'll actually be a foreign key to an actual job type table
GO
Then, for a couple of your rows, the data would be inserted like so:
INSERT INTO dbo.Job (JobID, YourNumericalLikeValue)
VALUES('P49384','22/10'),
('P9381',NULL);
GO
INSERT INTO dbo.JobTypeCompleted(JobID,JobType)
VALUES('P49384','Open vehicle bonnet'),
('P49384','BO'),
('P49384','dri'),
('P9381','vehicle'),
('P9381','pu'),
('P9381','bo'),
('P9381','dri');
Then you can easily get the jobs you want with a simple query:
SELECT J.JobID,
J.YourNumericalLikeValue
FROM dbo.Job J
WHERE EXISTS (SELECT 1
FROM dbo.JobTypeCompleted JTC
WHERE JTC.JobID = J.JobID
AND JTC.JobType = 'dri');
You can apply like operator in your query as column_name like '%-dri'. It means find out records that end with "-dri"

SQL create new unique random ID

I'm creating a Inventory application which uses an SQL database to keep track of products.
The ProductNumber is in the format yyyy-xxxx (i.e. 8024-1234), where the first 4 digits describe a category and the last 4 digits describe the an increasing integer, together creating the productnumber.
When creating a new product, the category should first be approved by an administrator, and therefor all new products will be added as 9999-xxxx. Then later, when the product is approved in the category, it's product number will change to the correct ProductNumber.
What I need for this is when creating a new product, to generate a random number for the last 4 digits, and then check if they don't exist already in the database (together with the first 4 digits). So, when creating a new product, some SQL query should create for example 9999-0123 and then double check if this one doesn't exist already.
How could one achieve this?
Thanks in advance!
you didn't precise the SGBD you are using, but here is a potential solution using Oracle PL/SQL:
declare
temp varchar2(10);
any_rows_found number;
row_exist boolean := true;
begin
WHILE row_exist = true
LOOP
temp := '9999-' || ceil(DBMS_RANDOM.value(low => 999, high => 9999));
select count(*) into any_rows_found from my_table where my_column = temp;
if any_rows_found = 1 then
else
row_exist := false;
insert into my_table values (..................., temp);
end if;
end loop;
end;
we use DBMS_RANDOM to generate the random value , concatenate it to 9999- and then check if it exists we loop to generate another value, if it doesn't exist we insert the value.
regards
You can generate your product number with a sequence, if you'd like an incremental number:
CREATE SEQUENCE product_number
START WITH 1000
INCREMENT BY 1
NOCACHE
NOCYCLE;
Whenever you insert or update a new product and need a valid number just call (sequence.nextVal). Then in your product table set (year, product_number) as a primary key (or the product number itself). If you can't set the primary key as said and want to check if the item already exist with the serial number you can generate the sequence number using:
SELECT sequence.nextVal FROM DUAL;
Then check if the product with the generated number exists.
Didn't know what dialect of SQL you are using, this is Oracle SQL but it can appliead in other dialects too.
Also not sure about the target DB - but worked it out for MS-SQL.
In the first step I would not reccommend the approach of generating a random number first and then check if this one exist and potentially doing this over and over again.
Instead you could go by and get the current max productnumber and work from there on. Even with a varchar you will retrieve the max int - since your syntax is always - (c = category / p = product). In addition to that you will get your desired value straight away since the target category is "9999".
You could work with something like this:
DECLARE #newID int;
-- REPLACE to remove the hyphen so we are facing an actual integer
-- Cast to be able to calculate with the value i.E. adding 1 on top of it
-- MAX for retrieving the max value
SELECT #newID = MAX(CAST(replace(ProductNumber,'-','') as int)) + 1 from Test
-- Set the ID by default to 99990000 in case there are no values with the 9999-prefix
IF #newID < 99990000
BEGIN
SET #newID = 99990000
END
-- Push back the hyphen into the new ID given you the final new productNumber
-- 5 is the starting index
-- 0 since no chars from the original ID shall be removed
Select STUFF(#newID, 5,0,'-')
So in case you currently have a product with 9999-1423 as your product with the highest number this would return "9999-1424".
If there are no products with the prefix of "9999" you would simply get "9999-0000".
The ProductNumber is in the format yyyy-xxxx (i.e. 8024-1234), where the first 4 digits describe a category and the last 4 digits describe the an increasing integer, together creating the productnumber.
We will implement this with a calculated column with puts together the category and the product number which will be in their own individual fields.
When creating a new product, the category should first be approved by an administrator, and therefor all new products will be added as 9999-xxxx. Then later, when the product is approved in the category, it's product number will change to the correct ProductNumber.
Put simply, by default every new product is automatically assigned product category 9999
What I need for this is when creating a new product, to generate a random number for the last 4 digits, and then check if they don't exist already in the database (together with the first 4 digits). So, when creating a new product, some SQL query should create for example 9999-0123 and then double check if this one doesn't exist already.
This can be implemented as an identity. This is not random, but I assume that is not really a requirement right?
Keep in mind there are many holes in these requirements.
If your product number changes from 9999-1234 to 8024-1234 but, has already appeared on reports / documents as 9999-1234, that's a problem
This format only supports at most 1,000 products. Then your system breaks
Again, does the number really need to be random?
I won't go into the actual mechanism for approval and assignment, you'll need to ask that in another question once this one is solved.
ProductNumber is in fact not a number, it's a code, so I don't agree with that column name
On to the code.
Create a table by running this:
CREATE TABLE dbo.Products
(
ProductID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
ProductName VARCHAR(100),
ProductCategoryID INT NOT NULL DEFAULT (9999),
ProductNumber AS (FORMAT(ProductCategoryID,'0000') + '-' + FORMAT(ProductID,'0000'))
)
Some explanation of the columns:
ProductID will autogenerate an incrementing number, starting at 1, incrementing by 1 each time. It's guaranteed to be unique. It's also defined as the primary key
ProductCategoryID will default to 9999 if you don't specify anything for it
ProductNumber is the special value you were after calculated from two individual columns
Now create a new product and see what happens
INSERT INTO dbo.Products(ProductName)
VALUES ('Brown Shoes')
SELECT * FROM dbo.Products
You can see Product Number 9999-0001
Add some more and note that the product code increments. It is not random. Carefully consider if you actually really need this to be random.
Now set the actual product category:
UPDATE dbo.Products
SET ProductCategoryID = 7 WHERE ProductID = 1
SELECT * FROM dbo.Products
and note that the product number updates.
Important to note that the real product id is actually just ProductID. The ProductCode column is just something to satisfy your requirements.

How to determine id date falls under one of the ranges (ranges are stored in separate rows of another table)

I have SalesFacts Table, which contains Sales_Amount, Customer_ID and Invoice_Date.
In another table I have Information's about special agreements for some of the customers (columns are: Customer_ID, Agreement_Start_Date, Agreement_End_Date).
Now - i would like to check, if the sales from SalesFact table occurred when special agreement was active for the Customer. This would be pretty easy, if there was only one date range when special agreement was active. However, in my case, Table with Special Agreements date ranges contains duplicated Customer ID, because for one Customer there might be several time ranges, where special agreement was active.
E.G. In SalesFact Table I have 3 transactions for one customer:
In SpacialAgreements Table I can see, that there are 2 data ranges when this customer had a right to special agreements.
I would like to create a query, that adds additional column to my SalesFacts table, that would determine, if the transaction happened when there was a Special Agreement Active. So in case shown above, it would be:
If there was Only one date range with special agreement it would be pretty easy:
Select
S.[Sales_Amount], S.[Customer_ID], S.[Invoice_Date],
IIF(S.[Invoice_Date] >= A.[Agreement_Start_Date] and S.[Invoice_Date]<=A.[Agreement_End_Date],'YES','NO') as AGREEMENT
From SalesFacts S left join SpacialAgreements A on S.[Customer_ID] = A.[Customer_ID]
But since there are several date ranges in SpacialAgreement table, i don't know how to achieve that properly, without risking any duplicates in Sales_Amount and without loosing any data.
Any ideas?
If you want to get data exactly as you shown in question then for the SELECT statement you can use something like this:
SELECT
S.[Sales_Amount],
S.[Customer_ID],
S.[Invoice_Date],
CASE WHEN EXISTS (SELECT 1
FROM SpacialAgreements A
WHERE A.Customer_ID = S.Customer_ID
AND S.[Invoice_Date] >= A.[Agreement_Start_Date]
AND S.[Invoice_Date] <= A.[Agreement_End_Date])
THEN 'YES'
ELSE 'NO'
END as Agreement
FROM SalesFacts S
So, this solution can be used if you are selecting data or creating view from this query.
If you want to have persisted value as one physical column in your SalesFacts table then you can try to solve your problem with triggers.

How to reset SQL Server 2008 Column based on years

I'm working on a leave software, and my problem is that i need to reset the leave days to default number of days (30 day) after one year. would you pleas help me with that.
ps: I'm using VB.NET AND SQL SERVER.
create table Addemployees
(
Fname varchar (500),
Lname varchar (500),
ID int not null identity(1, 1) primary key,
CIN varchar (500),
fromD date,
toD date,
Email varchar(500),
phone varchar(500),
Leave_num int
)
This is the tablet that contains the column Leave_num that has the leave numbers inserted by the user
update addemployees
set leave_num = 30
As for how you trigger this logic. There are many ways you could go about this. You'll need some sort of scheduler like an Agent job, or whatever else you have at your disposal to run this process on a recurring, scheduled, basis. The key thing is not to keep updating the LeaveNum if it's already been updated. You could maintain an extra column on each row indicating the last time they were reset. This is probably the simplest, but if it's truly an all-or-nothing type thing, and those dates will all be the same, that's sort of a waste of space.
You could then either create a separate table which just contains information about when these once-a-year jobs run, or something like an Extended Property (which is a little more involved to set up).
Whatever the solution you choose, Just save off the date (or even just the year), and then when your process runs, if the difference between the last update is greater than a year (or if the year of the last update is less than the current year) run your update, then update however you're storing that information; be it columns, a separate table, or an extended property.

How can I assign a number to each row in a table representing the record number?

How can I show the number of rows in a table in a way that when a new record is added the number representing the row goes higher and when a record is deleted the number gets updated accordingly?
To be more clear,suppose I have a simple table like this :
ID int (primary key) Name varchar(5)
The ID is set to get incremented by itself (using identity specification) so it can't represent the number of row(record) since if I have for example 3 records as:
ID NAME
1 Alex
2 Scott
3 Sara
and I delete Alex and Scott and add a new record it will be:
3 Sara
4 Mina
So basically I'm looking for a sql-side solution for doing this so that I don't change anything else in the source code in multiple places.
I tried to write something to get the job done but it failes. Here it is :
SELECT COUNT(*) AS [row number],Name
FROM dbo.Test
GROUP BY ID, Name
HAVING (ID = ID)
This shows as:
row number Name
1 Alex
1 Scott
1 Sara
while I want it to get shown as:
row number Name
1 Alex
2 Scott
3 Sara
If you just want the number against the rows while selecting the data and not in the database then you can use this
select row_number() over(order by id) from dbo.Test
This will give the row number n for nth row.
Try
SELECT id, name, ROW_NUMBER() OVER (ORDER BY id) AS RowNumber
FROM MyTable
What you want is called an auto increment.
For SQL-Server this is achieved by adding the IDENTITY(1,1) attribute to the table definition.
Other RDBMS use a different syntax. Firebird for example has generators, which do the counting. In a BEFORE-INSERT trigger you would assign the ID-field to the current value of the generator (which will be increased automatically).
I had this exact problem a while ago, but I was using SQL Server 2000, so although row number() is the best solution, in SQL Server 2000, this isn't available. A workaround for this is to create a temporary table, insert all the values with auto increment, and replace the current table with the new table in T-SQL.

Resources