RANK() Function on DATE IN SQL - sql

I am using PopSQL for this.
Please consider the following: I am trying to rank each country with PayDate. For each PayDate, there is more than one country linked. My wish is to group each country with specific PayDate or its count and rank them accordingly.
CREATE TABLE Country
(
CountryID INT PRIMARY KEY,
CountryName VARCHAR(40)
);
CREATE TABLE State
(
StateID INT PRIMARY KEY,
StateName VARCHAR(40),
CountryID INT,
FOREIGN KEY(CountryID) REFERENCES Country(CountryID)
);
CREATE TABLE City
(
CityID INT PRIMARY KEY,
CityName VARCHAR(40),
StateID INT,
FOREIGN KEY(StateID) REFERENCES State(StateID)
);
CREATE TABLE Rooms
(
RoomID INT PRIMARY KEY,
RoomTypeID INT,
RoomBandID INT,
RoomFacilityID INT,
CityID INT,
Floor INT,
AddionalNotes VARCHAR(255),
FOREIGN KEY(RoomTypeID) REFERENCES RoomType(RoomTypeID),
FOREIGN KEY(RoomBandID) REFERENCES RoomBand(RoomBandID),
FOREIGN KEY(RoomFacilityID) REFERENCES RoomFacility(RoomFacilityID),
FOREIGN KEY(CityID) REFERENCES City(CityID)
);
CREATE TABLE DT_Date
(
DateID INT NOT NULL,
FullDate Datetime NOT NULL,
DateMonth INT NOT NULL,
Quarter INT NOT NULL,
DateYear year NOT NULL,
PRIMARY KEY(DateID)
);
CREATE TABLE Customer
(
CustomerID INT PRIMARY KEY,
CustomerForename VARCHAR(20),
CustomerSurname VARCHAR(20),
CustomerDOB DATetime,
CustomerHomePhone INT,
CustomerMobilePhone INT,
CustomerWorkPhone INT,
CustomerEmail VARCHAR(40),
CityID INT,
FOREIGN KEY (CityID) REFERENCES City(CityID)
);
CREATE TABLE Payments
(
PaymentID INT PRIMARY KEY,
PaymentComment VARCHAR(255),
PaymentsMethodID INT,
PayDate Datetime,
RoomID INT,
DateID INT,
CustomerID INT,
Price INT,
PaymentAmount INT,
FOREIGN KEY(RoomID) REFERENCES Rooms(RoomID),
FOREIGN KEY(PaymentsMethodID) REFERENCES PaymentsMethod(PaymentsMethodID),
FOREIGN KEY(DateID) REFERENCES DT_Date(DateID),
FOREIGN KEY(CustomerID) REFERENCES Customer(CustomerID)
);
Please also have a look at my last attempt:
select
rank() over (PARTITION BY CountryName order by count(PayDate)),
CountryName, PayDate, count(Paydate)
from
City, Payments, Rooms, Customer, State, Country, DT_Date
where
Payments.PayDate >= "2010-00-00 00:00:00"
and Payments.CustomerID = Customer.CustomerID
and State.CountryID = Country.CountryID
and City.StateID = State.StateID
and Customer.CityID = City.CityID
and Payments.DateID = DT_Date.DateID
group by
CountryName, PayDate;
The query worked. However, results are not correct because there are two PayDates for UK and one PayDate for USA. Following is the result shown:
Rank CountryName PayDate count(Paydate)
1 UK 2015-12-31 00:00:00 4
1 UK 2014-06-10 00:00:00 4
1 USA 2011-11-25 00:00:00 4
Expected output:
Rank CountryName
1 UK
2 USA

Don't group by PayDate, only by CountryName. This means you cannot include the PayDate in the results, but you don't want that anyway.
Also remove CountryName from your partitioning. Your rank is just ordered by the PayDate count.
And to have the largest count ranked first, use a descending order.
select
rank() over (order by count(PayDate) desc ) as "rank",
CountryName
from City , Payments,Rooms,Customer,State,Country,DT_Date
WHERE Payments.PayDate >= '2010-00-00 00:00:00'
ANd Payments.CustomerID = Customer.CustomerID
And State.CountryID = Country.CountryID
AND City.StateID = State.StateID
And Customer.CityID = City.CityID
and Payments.DateID = DT_Date.DateID
GROUP by CountryName
Here's a simplified demo.
A few side notes.
Though it might work in your SQL implementation, 2010-00-00 00:00:00 is a dodgy datetime. Use 2010-01-01 00:00:00 or better your database's equivalent of year(Payments.PayDate) >= 2010
Don't group by CountryName, group by Country.CountryId. Grouping by name invites accidentally grouping two things together, and unique country names are not enforced in the schema (they should be).
Finally, explicit joins do make it easier to understand and maintain a query.

Related

Direct relation in SQL

I'm a beginner in SQL and I'm trying to make a small database, but I was asked to make a direct relation between OrderNumber in Orders table and CustomerNumber in Customers table, aren't I using a direct relation in here?.
Here is my code:
Customers table:
CREATE TABLE Customers
(
CustomerNumber VARCHAR(25) PRIMARY KEY NOT NULL,
CustomerName VARCHAR(50),
Phone INT NOT NULL,
Country VARCHAR(50),
City VARCHAR(50),
State VARCHAR(50),
PostalCode VARCHAR(5) NOT NULL
);
Orders table:
CREATE TABLE Orders
(
OrderNumber VARCHAR(25) ,
CustomerNumber VARCHAR(25) PRIMARY KEY NOT NULL,
ProductName VARCHAR(200) NOT NULL UNIQUE,
OrderDate DATE NOT NULL,
requiredDate DATE NOT NULL,
Status VARCHAR(50)
);
I believe, you probably want something like this:
CREATE TABLE Customers
(
CustomerNumber INT PRIMARY KEY NOT NULL,
CustomerName VARCHAR(50),
Phone INT NOT NULL,
Country VARCHAR(50),
City VARCHAR(50),
State VARCHAR(50),
PostalCode VARCHAR(5) NOT NULL
);
CREATE TABLE Orders
(
OrderNumber INT PRIMARY KEY NOT NULL,
CustomerNumber INT NOT NULL FOREIGN KEY REFERENCES Customers(CustomerNumber),
ProductName VARCHAR(200) NOT NULL UNIQUE,
OrderDate DATE NOT NULL,
requiredDate DATE NOT NULL,
Status VARCHAR(50)
);
Changed the datatype of CustomerNumber and OrderNumber to INT
Made Orders.CustomerNumber a foreign key with a relation to the Customer table (CustomerNumber column)

DateDiff asc and desc

SELECT DISTINCT
at.AccountId AS AccountId,
a.FirstName + ' ' + a.LastName AS [FullName],
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) AS LongestTrip,
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) AS ShortestTrip
FROM
Accounts a
JOIN
AccountsTrips at ON a.Id = AT.AccountId
JOIN
Trips t ON T.Id = AT.TripId
WHERE
a.MiddleName IS NULL AND t.CancelDate IS NULL
ORDER BY
DATEDIFF(day, T.ArrivalDate, T.ReturnDate) DESC, ShortestTrip ASC
The code only orders the tables descending in LongestTrip and in ShortestTrip
SAMPLE DATA !
Find the longest and shortest trip for each account, in days. Filter the results to accounts with no middle name and trips, which are not cancelled (CancelDate is null).
Order the results by Longest Trip days (descending), then by Shortest Trip (ascending).
Examples
AccountId FullName LongestTrip ShortestTrip
------------------------------------------------------------
40 Winna Maisey 7 1
56 Tillie Windress 7 1
57 Eadith Gull 7 1
66 Sargent Rockhall 7 1
69 Jerome Flory 7 2
… … … …
The Tables are --
CREATE TABLE Cities
(
Id INT PRIMARY KEY IDENTITY,
Name NVARCHAR(20) NOT NULL,
CountryCode CHAR(2) NOT NULL
)
CREATE TABLE Hotels
(
Id INT PRIMARY KEY IDENTITY,
Name NVARCHAR(30) NOT NULL,
CityId INT FOREIGN KEY REFERENCES Cities(Id) NOT NULL,
EmployeeCount INT NOT NULL,
BaseRate DECIMAL(10,2)
)
CREATE TABLE Rooms
(
Id INT PRIMARY KEY IDENTITY,
Price DECIMAL(10,2) NOT NULL,
Type NVARCHAR(20) NOT NULL,
Beds INT NOT NULL,
HotelId INT FOREIGN KEY REFERENCES Hotels(Id) NOT NULL
)
CREATE TABLE Trips
(
Id INT PRIMARY KEY IDENTITY,
RoomId INT FOREIGN KEY REFERENCES Rooms(Id) NOT NULL,
BookDate DATE NOT NULL, CHECK(BookDate<ArrivalDate),
ArrivalDate DATE NOT NULL, CHECK(ArrivalDate<ReturnDate),
ReturnDate DATE NOT NULL,
CancelDate DATE
)
CREATE TABLE Accounts
(
Id INT PRIMARY KEY IDENTITY,
FirstName NVARCHAR(50) NOT NULL,
MiddleName NVARCHAR(20),
LastName NVARCHAR(50) NOT NULL,
CityId INT NOT NULL,
BirthDate DATE NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL
CONSTRAINT FK_CityId FOREIGN KEY (CityId)
REFERENCES Cities(Id)
)
CREATE TABLE AccountsTrips
(
AccountId INT FOREIGN KEY REFERENCES Accounts(Id) NOT NULL,
TripId INT FOREIGN KEY REFERENCES Trips(Id) NOT NULL,
Luggage INT NOT NULL, CHECK(Luggage >= 0)
)
You want to pick the longest and shortest trips per account from your data. As all you want to get from the trips is the duration, you can simply aggregate and show MIN and MAX duration:
SELECT
a.Id AS AccountId,
a.FirstName + ' ' + a.LastName AS [FullName],
MIN(DATEDIFF(day, T.ArrivalDate, T.ReturnDate)) AS LongestTrip,
MAXD(ATEDIFF(day, T.ArrivalDate, T.ReturnDate)) AS ShortestTrip
FROM
Accounts a
JOIN
AccountsTrips at ON a.Id = AT.AccountId
JOIN
Trips t ON T.Id = AT.TripId
WHERE
a.MiddleName IS NULL AND t.CancelDate IS NULL
GROUP BY
a.Id, a.FirstName, a.LastName
ORDER BY
LongestTrip DESC, ShortestTrip ASC;
If you wanted to show additional data from the trips, you would use window functions (probably MIN OVER and MAX OVER) and would then either show two rows per account or aggregate these two rows.

How to get details for a customer that is both a seller and a buyer

I have a database which contains customers that are both buyers and sellers.
the customers can buy and sell houses.
I need to get details for the customers separately and still have a connection between them so i could know which customer sold to which customer.
In case a customer buys a house from a different customer how can i get the details for each customer separately?
The table I made so far for the customers is :
CREATE TABLE Customers (
SellerID int,
BuyerID int,
HouseID int,
SaleID int,
FirstName varchar (50),
LastName varchar (50),
Adress varchar (50),
BirthDate Date,
City varchar (50),
HomePhone varchar (50),
PRIMARY KEY (SellerID, BuyerID),
FOREIGN KEY (HouseID) REFERENCES House(HouseID),
FOREIGN KEY (SaleID) REFERENCES Sale(SaleID),
);
I have a sale table
CREATE TABLE Sale (
SaleID int,
SalesManID int,
SaleDate Date,
SalePrice int,
PRIMARY KEY (SaleID),
FOREIGN KEY (SalesManID) REFERENCES SalesMan(SalesManID),
);
I have a table that contains both sale and customers
CREATE TABLE SaleToCustomers (
SaleID int,
CustomersID int,
PRIMARY KEY (SaleID, CustomersID)
);
Your entities are confused. A customer should be a person, with information about the persion. Then Sales should refer to it twice, once for buyers and o once for sellers:
CREATE TABLE Customers (
CustomerId int PRIMARY KEY,
FirstName varchar(50),
LastName varchar(50),
Adress varchar(50),
BirthDate Date,
City varchar(50),
HomePhone varchar(50)
);
CREATE TABLE Sales (
SaleID int PRIMARY KEY,
SellerId int,
BuyerId int,
HouseId int,
SaleDate Date,
SalePrice int,
FOREIGN KEY (SellerId) REFERENCES Customers(CustomerId),
FOREIGN KEY (BuyerId) REFERENCES Customers(CustomerId),
FOREIGN KEY (HouseID) REFERENCES House(HouseID)
);
In other words, "buyer" and "seller" are attributes of a sale, not of a person.

How to resolve this fan trap in sql design

I am having this 3 table, which is available Trip , country , and location. the relation in my ERD design is
Location | M-1 | Country | 1-M | Available Trip
This is my attempt for the resolution
create table country
(
countryID int not null IDENTITY(1,1) PRIMARY KEY,
countryName varchar(50),
passportRegulation text,
currency varchar(20),
)
create table location
(
locationID int not null IDENTITY(1,1) PRIMARY KEY,
locationName varchar(100),
countryID int references country(countryID)on delete cascade
)
create table availableTrip
(
availableTripID int not null IDENTITY(1,1) PRIMARY KEY,
countryID int references country(countryID),
locationID int references location(locationID)
)
Is it possible to be correct if i having two foreign key to added into available trip , however i feel this is redundant because based on location i can know the country. I am kind of lost my direction for this design
Assuming that countryId needs to match the country in locationId, then you only want locationId in availableTrip.
You would look up the appropriate country using joins:
select avt.*, l.countryid
from availabletrip avt join
location l
on avt.locationid = l.locationid;
If the countryId could be different from the locationId, then you would want two columns.

Join three tables based on a single common column and select max date value

I have SQL Server tables defined like this:
create table users
(
user_id int NOT NULL IDENTITY(1,1),
name varchar(200),
username varchar(200),
password varchar(50),
encrypted_password varchar(100),
email varchar(30),
phone_no int,
address varchar(200),
PRIMARY KEY(user_id)
)
create table electricity
(
table_name VARCHAR(50),
electricity_bill_id int NOT NULL IDENTITY(1,1),
billing_month_year varchar(50),
units_consumed int,
user_id int,
account_number varchar(100),
amount varchar(100),
due_date varchar(100),
PRIMARY KEY(electricity_bill_id),
FOREIGN KEY(user_id) REFERENCES users(user_id)
);
insert into electricity (table_name, billing_month_year, units_consumed, user_id, account_number, amount, due_date)
values ('Electricity','2015-05-22',13,1,'acc01',2500,'2015-06-22');
create table water
(
table_name VARCHAR(10),
water_bill_id int NOT NULL IDENTITY(1,1),
billing_month_year_date varchar(50),
units_consumed int,
user_id int,
account_number varchar(100),
amount varchar(100),
due_date varchar(100),
PRIMARY KEY(water_bill_id),
FOREIGN KEY(user_id) REFERENCES users(user_id)
)
insert into water(table_name, billing_month_year_date, units_consumed, user_id, account_number, amount, due_date)
values ('Water','2015-04-22',17,1,'acc01',2500,'2015-05-14');
create table telephone
(
table_name VARCHAR(10),
telecom_bill_id int NOT NULL IDENTITY(1,1),
bill_Period varchar(100),
user_id int,
account_number varchar(100),
amount varchar(100),
issued_date varchar(50),
due_date varchar(50),
PRIMARY KEY(telecom_bill_id),
FOREIGN KEY(user_id) REFERENCES users(user_id)
)
insert into telephone(table_name, bill_Period, user_id, account_number, amount, issued_date, due_date)
values ('Telephone', '2015-04-22', 1, 'acc01', 2500, '2015-05-14', '2015-05-18');
The problem is that I want to join electricity, water and telephone tables based on the user id that I need to pass in to the query.
And also I want to pick the maximum due_date from the three tables. But, I've some issues with it.
Please can somebody help me.
One method is to use outer apply:
select u.*, e.*, w.*, t.*
from users u outer apply
(select top 1 e.*
from electricity e
where e.user_id = u.user_id
order by e.due_date desc
) e outer apply
(select top 1 w.*
from water w
where w.user_id = u.user_id
order by w.due_date desc
) w outer appy
(select top 1 t.*
from telephone t
where t.user_id = u.user_id
order by t.due_date desc
) t
where u.user_id = #user_id;
With indexes on (user_id, due_date) on all three tables, the query should have very good performance as well.
If I understand your question well, you want to do something like this:
SELECT MAX(e.due_date) AS ElectricityDueDate, MAX(w.due_date) AS WaterDueDate, MAX(t.due_date) AS TelephoneDueDate
FROM user u, electricity e, water w, telephone t
WHERE u.user_id=#user_id AND
e.user_id=u.user_id AND
w.user_id=u.user_id AND
t.user_id=u.user_id AND
GROUP BY u.user_id
However, to do so you should change the due_date type to DATE, in this way
you can compute the max value.