Multi-Query Queries [closed] - sql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
There are several tables in the database: films, persons and creators. Creators has two foreign keys to films and persons, as well as the fields "Character of participation" (director, actor, composer, etc.) and "Role" (see CREATE TABLE statements below). I would like to display a list of films in which the director is at the same time performing one of the main roles, indicating his last name and the role he played.
create table films (
film_id tinyint identity(1, 1),
film_name varchar(20) not null,
film_studio varchar(25) not null,
film_year int not null,
film_country varchar(20) null,
film_length tinyint not null,
film_genre varchar(15) not null,
constraint PK_films primary key(film_id),
);
create table persons (
person_id tinyint identity(1, 1),
person_name varchar(50) not null,
person_bday date not null,
person_dday date,
constraint PK_persons primary key(person_id),
);
create table creators (
creator_id tinyint identity(1, 1),
creator_film tinyint not null,
creator_person tinyint not null,
creator_who varchar(20) not null,
creator_role varchar(20),
constraint PK_creators primary key(creator_id),
constraint FK_CF foreign key(creator_film) references films(film_id),
constraint FK_CP foreign key(creator_person) references persons(person_id),
constraint CH_Who check(creator_who='director' or creator_who='actor' or creator_who='composer'),
);
insert into films(film_name, film_studio, film_year, film_country, film_length, film_genre) values ('Film1', 'Studio1', 2018, 'USA', 100, 'Genre1')
insert into films(film_name, film_studio, film_year, film_country, film_length, film_genre) values ('Film2', 'Studio2', 2018, 'USA', 120, 'Genre2')
insert into films(film_name, film_studio, film_year, film_country, film_length, film_genre) values ('Film3', 'Studio3', 2000, 'England', 90, 'Genre3')
insert into persons(person_name, person_bday, person_dday) values ('John Smitt', '1988-12-12', null)
insert into persons(person_name, person_bday, person_dday) values ('Mel Gibson
', '1988-12-12', null)
insert into persons(person_name, person_bday, person_dday) values ('Miley Cyrus', '2001-12-12', null)
insert into persons(person_name, person_bday, person_dday) values ('Deadpool', '1999-12-12', null)
insert into creators(creator_film, creator_person, creator_who) values (1, 1, 'Director')
insert into creators(creator_film, creator_person, creator_who, creator_role) values (1, 1, 'Actor', 'Main')
insert into creators(creator_film, creator_person, creator_who, creator_role) values (2, 3, 'Actor', 'Secondary')
insert into creators(creator_film, creator_person, creator_who, creator_role) values (3, 4, 'Actor', 'Secondary')
Desired result is: Film1 John Smith Main

http://sqlfiddle.com/#!18/9211f/6
SELECT
films.film_name,
persons.person_name,
actor.creator_role
FROM creators director
INNER JOIN creators actor
ON director.creator_person = actor.creator_person
AND actor.creator_who = 'Actor'
LEFT JOIN films
ON director.creator_film = films.film_id
LEFT JOIN persons
ON director.creator_person = persons.person_id
WHERE director.creator_who = 'Director'

Related

SQL inner join condition by actual date

I need to join actual document to people. Documents has date issued (passport, for example).
SQL Fiddle: http://sqlfiddle.com/#!9/3a8118/2/0
Table structure:
CREATE TABLE people
(
p_id INT NOT NULL AUTO_INCREMENT,
p_name VARCHAR(50) NOT NULL,
PRIMARY KEY(p_id)
);
INSERT INTO people (p_id, p_name)
VALUES (1, 'Name_1'),
(2, 'Name_2');
CREATE TABLE documents
(
d_id INT NOT NULL AUTO_INCREMENT,
d_people INT(10) NOT NULL,
d_date VARCHAR(10) NOT NULL,
PRIMARY KEY(d_id)
);
INSERT INTO documents (d_id, d_people, d_date)
VALUES (1, 1, '01.01.2022'),
(2, 2, '01.12.2021'),
(3, 1, '05.02.2022'),
(4, 1, '10.02.2022'),
(5, 2, '04.01.2022'),
(6, 1, '20.01.2022');
Query: condition is select actual document when date is 21.01.2022, it must return d_id = 6:
SELECT *
FROM people
INNER JOIN documents ON d_people = p_id
WHERE p_id = 1 AND ??? d_date 21.01.2022 ???
;
I need to do an inner join to return only this row:
use this query:
Fiddle
SELECT * FROM people
INNER JOIN documents ON d_people = p_id
WHERE p_id = 1 and d_date='20.01.2022';

SQL Count returns wrong value in my INNER JOIN Statement

I am quite new to SQL and I am having some difficulty with my COUNT() feature.
It keeps returning the wrong COUNT() value as I am trying to calculate the TOTAL number of specific cars sold by users. My tables and results are here:
http://sqlfiddle.com/#!9/d2ef0/5
My Schema:
CREATE TABLE IF NOT EXISTS Users(
userID INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
forename VARCHAR(50) NOT NULL,
surname VARCHAR(50) NOT NULL,
PRIMARY KEY (userID)
);
CREATE TABLE IF NOT EXISTS CarType(
carTypeID INT NOT NULL AUTO_INCREMENT,
description VARCHAR(80),
PRIMARY KEY (carTypeID)
);
CREATE TABLE IF NOT EXISTS Country(
countryID INT NOT NULL AUTO_INCREMENT,
name VARCHAR(100),
PRIMARY KEY (countryID)
);
CREATE TABLE IF NOT EXISTS Cars(
carID INT NOT NULL AUTO_INCREMENT,
carTypeID INT NOT NULL,
countryID INT NOT NULL,
description VARCHAR(100) NOT NULL,
make VARCHAR(100) NOT NULL,
model VARCHAR(100),
FOREIGN KEY (carTypeID) REFERENCES CarType(carTypeID),
FOREIGN KEY (countryID) REFERENCES Country(countryID),
PRIMARY KEY (carID)
);
CREATE TABLE IF NOT EXISTS Likes(
userID INT NOT NULL,
carID INT NOT NULL,
likes DOUBLE NOT NULL,
FOREIGN KEY (userID) REFERENCES Users(userID),
FOREIGN KEY (carID) REFERENCES Cars(carID)
);
CREATE TABLE IF NOT EXISTS Sold(
userID INT NOT NULL,
carID INT NOT NULL,
FOREIGN KEY (userID) REFERENCES Users(userID),
FOREIGN KEY (carID) REFERENCES Cars(carID)
);
INSERT INTO Users VALUES
(NULL, "micheal", "Micheal", "Sco"),
(NULL, "bensco", "Ben", "Sco"),
(NULL, "shanemill", "Shane", "Miller");
INSERT INTO CarType VALUES
(NULL, "Saloon"),
(NULL, "HatchBack"),
(NULL, "Low Rider");
INSERT INTO Country VALUES
(NULL, "UK"),
(NULL, "USA"),
(NULL, "JAPAN"),
(NULL, "GERMANY");
INSERT INTO Cars VALUES
(NULL, 1, 2, "Ford Mustang lovers", "Mustang", "Ford"),
(NULL, 2, 3, "Drift Kings", "Skyline", "Nissan"),
(NULL, 3, 1, "British classic", "Cooper", "Mini");
INSERT INTO Likes VALUES
(1, 1, 3),
(1, 2, 2),
(2, 3, 5),
(2, 3, 7),
(2, 3, 1),
(2, 3, 2);
INSERT INTO Sold VALUES
(1, 2),
(1, 3),
(1, 1),
(2, 2),
(2, 3),
(3, 1),
(3, 3);
This is the Sold table:
userID carID
1 2
1 3
1 1
2 2
2 3
3 1
3 3
This is my complex query:
SELECT DISTINCT Cars.carID, Cars.description, Cars.model, Country.name,
CarType.description, ROUND(AVG(Likes.likes)), COUNT(*)
FROM Cars
INNER JOIN Sold ON
Cars.carID = Sold.carID
INNER JOIN Country ON
Cars.countryID = Country.countryID
INNER JOIN CarType ON
Cars.carTypeID = CarType.carTypeID
INNER JOIN Likes ON
Cars.carID = Likes.carID
GROUP BY Cars.carID
The actual result from this complex SQL Query:
carID description model name description ROUND(AVG(Likes.likes)) COUNT(*)
1 Ford Mustang lovers Ford USA Saloon 3 2
2 Drift Kings Nissan JAPAN HatchBack 2 2
3 British classic Mini UK Low Rider 4 12
For example, the result for the last one is incorrect - it should not be 12
Would be nice if someone could tell me where I went wrong
Thanks
You are attempting to aggregate across two different dimensions -- Sold and Likes. The result is a Cartesian product of rows for each car, and this throws off the aggregations.
The solution is the pre-aggregate the results along each dimension:
SELECT c.carID, c.description, c.model, cy.name, ct.description,
l.avgLikes, s.NumSold
FROM Cars c INNER JOIN
(SELECT s.CarId, COUNT(*) as NumSold
FROM Sold s
GROUP BY s.CarId
) s
ON c.carID = s.carID INNER JOIN
Country cy
ON c.countryID = cy.countryID INNER JOIN
CarType ct
ON c.carTypeID = ct.carTypeID LEFT JOIN
(SELECT l.carId, AVG(Likes) as avgLikes
FROM Likes l
GROUP BY CarId
) l
ON c.carID = l.carID;
Here is the SQL Fiddle.
If all you want is the total number of specific cars sold by user then all your info is in the sold table. This query will give you what you want by carID. You can use that as a subquery if you want to join on other tables to get more info.
SELECT userID, carID, count(*) as totalSold FROM Sold GROUP BY userID, carID;

How to automatically generate unique id in SQL like UID12345678?

I want to automatically generate unique id with per-defined code attach to it.
ex:
UID12345678
CUSID5000
I tried uniqueidentifier data type but it generate a id which is not suitable for a user id.
Any one have suggestions?
The only viable solution in my opinion is to use
an ID INT IDENTITY(1,1) column to get SQL Server to handle the automatic increment of your numeric value
a computed, persisted column to convert that numeric value to the value you need
So try this:
CREATE TABLE dbo.tblUsers
(ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
UserID AS 'UID' + RIGHT('00000000' + CAST(ID AS VARCHAR(8)), 8) PERSISTED,
.... your other columns here....
)
Now, every time you insert a row into tblUsers without specifying values for ID or UserID:
INSERT INTO dbo.tblUsersCol1, Col2, ..., ColN)
VALUES (Val1, Val2, ....., ValN)
then SQL Server will automatically and safely increase your ID value, and UserID will contain values like UID00000001, UID00000002,...... and so on - automatically, safely, reliably, no duplicates.
Update: the column UserID is computed - but it still OF COURSE has a data type, as a quick peek into the Object Explorer reveals:
CREATE TABLE dbo.tblUsers
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
UserID AS 'UID' + RIGHT('00000000' + CAST(ID AS VARCHAR(8)), 8) PERSISTED,
[Name] VARCHAR(50) NOT NULL,
)
marc_s's Answer Snap
Reference:https://learn.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql?view=sql-server-2017
-- Creating a table using NEWID for uniqueidentifier data type.
CREATE TABLE cust
(
CustomerID uniqueidentifier NOT NULL
DEFAULT newid(),
Company varchar(30) NOT NULL,
ContactName varchar(60) NOT NULL,
Address varchar(30) NOT NULL,
City varchar(30) NOT NULL,
StateProvince varchar(10) NULL,
PostalCode varchar(10) NOT NULL,
CountryRegion varchar(20) NOT NULL,
Telephone varchar(15) NOT NULL,
Fax varchar(15) NULL
);
GO
-- Inserting 5 rows into cust table.
INSERT cust
(CustomerID, Company, ContactName, Address, City, StateProvince,
PostalCode, CountryRegion, Telephone, Fax)
VALUES
(NEWID(), 'Wartian Herkku', 'Pirkko Koskitalo', 'Torikatu 38', 'Oulu', NULL,
'90110', 'Finland', '981-443655', '981-443655')
,(NEWID(), 'Wellington Importadora', 'Paula Parente', 'Rua do Mercado, 12', 'Resende', 'SP',
'08737-363', 'Brasil', '(14) 555-8122', '')
,(NEWID(), 'Cactus Comidas para Ilevar', 'Patricio Simpson', 'Cerrito 333', 'Buenos Aires', NULL,
'1010', 'Argentina', '(1) 135-5555', '(1) 135-4892')
,(NEWID(), 'Ernst Handel', 'Roland Mendel', 'Kirchgasse 6', 'Graz', NULL,
'8010', 'Austria', '7675-3425', '7675-3426')
,(NEWID(), 'Maison Dewey', 'Catherine Dewey', 'Rue Joseph-Bens 532', 'Bruxelles', NULL,
'B-1180', 'Belgium', '(02) 201 24 67', '(02) 201 24 68');
GO
If you want to add the id manually you can use,
PadLeft() or String.Format() method.
string id;
char x='0';
id=id.PadLeft(6, x);
//Six character string id with left 0s e.g 000012
int id;
id=String.Format("{0:000000}",id);
//Integer length of 6 with the id. e.g 000012
Then you can append this with UID.
The 'newid()' method unique id generate for per record.
AddColumn("dbo.Foo", "Key", c => c.String(nullable: false, maxLength: 250, defaultValueSql: "newid()"));
Table Creating
create table emp(eno int identity(100001,1),ename varchar(50))
Values inserting
insert into emp(ename)values('narendra'),('ajay'),('anil'),('raju')
Select Table
select * from emp
Output
eno ename
100001 narendra
100002 rama
100003 ajay
100004 anil
100005 raju

SQL performance width versus depth

I have a customers table that holds information about customer prefrences like if he wants to receive a newsletter and so on. If he/she wants to receive a newsletter, the value is stored in the column "customerNewsletter" and set to true. However I have a couple of these bit values and parameters that are in a column of there own. I store dates, true/false, integers and tekst like this for each customer.
I find that about 80% of my customers wants to receive a newsletter and that makes that 80% of the values is set to true. I now store a value for each customer set to false or true. What if I only should have to store the 20% set to false ??
There is a list of about 20 of these parameters that I could include as a column (they are now), but I was wondering if there is a better way.
So I create 3 tables to hold these parameter values, a param table holding the actual value, a paramsNames table, that holds the names of the values and a params table that connects the parameters to a customerID
SELECT
customerParamsName as [Name],
customerParamText as [Text],
customerParamINT as [int],
customerParamsDateTime as [Date]
FROM db14.customerParams
INNER JOIN db14.customerParam ON customerParamsChildID = customerParamID
INNER JOIN db14.customerParamsNames ON customerParamNameID = customerParamsNameID
This would give me
Name Text int Date
Phonenumber NULL 615164898 2013-09-20 00:00:00.000
Can anyone tell me if this is a good way to go, or are there more common ways of storing Multi-Type parameters more efficiently ?
AFTER some MORE consideration
I have created 2 tables
customerParam
paramID paramNameID ParamParentID paramChildID paramText paramINT paramDate
INT TINYINT INT INT varchar(24) INT DATETIME
PRIMARY INDEXED
customerParamNames
paramNameID paramName
TINYINT VARCHAR(24)
PRIMARY
1 'FirstName'
2 'LastName'
3 'Email Address'
4 'Phonenumber'
5 etc..
Let's say I want to store the firstName and LastName
I create records in customerParam for both values ;
paramID paramNameID ParamParentID paramChildID paramText paramINT paramDate
17456 1 'John'
17467 2 'Doo'
17468 1 752 17456
17469 2 752 17467
As I expect more occurrences for the name ‘John’ I am storing it as an independent value, then joining it using the parentID/ChildID combination.
and for the phoneNumber
17470 4 752 31615164899
17471 5 752 'me#here.com'
The phonenumber is very explicit to this customer, I am using the parentID to join it straight to the customer. The same goes for the emailaddress.
At this time this solution looks like the way to go... I am also still looking at the xml approach but I don’t have a good understanding on how to use XQuery and xmlDocuments stored in a database.
And It seems like a lot of overhead.
I will move forward with the solution above... until someone gives me a better one.
Example SQL
DECLARE #paramNames TABLE (paramNameID TINYINT, paramName varchar(24))
DECLARE #param TABLE (paramID INT, paramNameID TINYINT, paramParentID INT, paramChildID INT, paramText varchar(24), paramINT INT, paramDate datetime)
INSERT INTO #paramNames VALUES ( 1, 'firstname')
INSERT INTO #paramNames VALUES ( 2, 'lastname')
INSERT INTO #paramNames VALUES ( 3, 'emailaddress')
INSERT INTO #paramNames VALUES ( 4, 'phonenumber')
select * from #paramNames
INSERT INTO #param VALUES (1, 1, Null, Null, 'John' , Null, Null)
INSERT INTO #param VALUES (2, 2, Null, Null, 'Doo' , Null, Null)
INSERT INTO #param VALUES (3, 1, 752, 1, Null , Null, Null)
INSERT INTO #param VALUES (4, 2, 752, 2, Null , Null, Null)
INSERT INTO #param VALUES (5, 4, 752, Null, Null , 615164899, Null)
INSERT INTO #param VALUES (5, 3, 752, Null, 'me#here.com' , Null, Null)
select
a.paramParentID, b.paramName, c.paramText, c.paramINT, c.paramDate
from #param a
inner join #paramNames b on a.paramNameID = b.paramNameID
inner join #param c on a.paramChildID = c.paramID
UNION ALL
select
a.paramParentID, b.paramName, a.paramText, a.paramINT, a.paramDate
from #param a
inner join #paramNames b on a.paramNameID = b.paramNameID
WHERE paramParentID IS NOT NULL
AND paramChildID IS NULL
giving the result
paramParentID paramName paramText paramINT paramDate
752 firstname John NULL NULL
752 lastname Doo NULL NULL
752 phonenumber NULL 615164899 NULL
752 emailaddress me#here.com NULL NULL
I would approach this a little differently if you have performance and flexibility in mind.
USE Test;
CREATE TABLE Customers
(
CustomerID INT NOT NULL CONSTRAINT PK_Customers
PRIMARY KEY CLUSTERED IDENTITY(1,1)
, CustomerName NVARCHAR(255)
);
CREATE TABLE CustomersReceivingEmails
(
CustomerID INT NOT NULL CONSTRAINT FK_CustomerID
FOREIGN KEY REFERENCES Customers (CustomerID)
ON DELETE CASCADE ON UPDATE CASCADE
, EmailAddress NVARCHAR(255) NOT NULL
CONSTRAINT PK_CustomersReceivingEmails
PRIMARY KEY CLUSTERED (CustomerID, EmailAddress)
);
INSERT INTO Customers (CustomerName) VALUES ('Max');
INSERT INTO Customers (CustomerName) VALUES ('Mike');
INSERT INTO CustomersReceivingEmails (CustomerID, EmailAddress)
VALUES (1, 'us#them.com');
INSERT INTO CustomersReceivingEmails (CustomerID, EmailAddress)
VALUES (1, 'us#me.com');
/* ALL Customers */
SELECT * FROM Customers;
/* Only customers who wish to receive Emails, allows a given customer
to have multiple email addresses */
SELECT C.CustomerName, E.EmailAddress
FROM Customers C
INNER JOIN CustomersReceivingEmails E ON C.CustomerID = E.CustomerID
ORDER BY C.CustomerName, E.EmailAddress;
The SELECT returns rows like this:
This allows the Customers table to contain all customers regardless of their preference for emails.
The CustomersReceivingEmails table has a foreign key to Customers.CustomerID for customers who want to receive emails.
Your second solution is a variant of what is commonly known as Entity-Attribute-Value data model. This approach appears to be flexible. However, it essentially generates a schema within schema and is very slow to query as the number of attributes increases
If you're storing a lot of identical values, have a look at columnstore indexes. They work well in scenarios where selectivity is low (lots of rows & only a small number of distinct values)

Need Help Writing SQL Query in Postgresql

I've been trying to write this query but can't seem to get it right for some reason or another..
What I need to do is:
Change the status of a question to 'closed' if there has not been an update associated with this question inserted into the qUpdateTable in the last 24 hours.
I only want it to be closed if a staff member has replied to it at least once.
You can determine if a staff member or a user has replied to the question by checking the qUpdateTable and seeing if a the StaffID field is empty or has a value for that particular tickets updates. If there is a staffID then it has been updated by a staff member, however if it does not then the qUpdate was done by a user.
Essentialy the way this works is a user posts a question by inserting into the Question table, and replies are made by inserting into the qUpdate table and linked to the original question using the foreign key - "QuestionID".
The tables:
CREATE TABLE Staff
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Customer
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Email VARCHAR(40) NOT NULL
);
CREATE TABLE Product
(
ID INTEGER NOT NULL PRIMARY KEY,
Name TEXT NOT NULL
);
CREATE TABLE Question
(
ID INTEGER NOT NULL PRIMARY KEY,
Problem VARCHAR(1000),
Status VARCHAR(20) NOT NULL DEFAULT 'open',
Priority INTEGER NOT NULL,
LoggedTime TIMESTAMP NOT NULL,
CustomerID INTEGER NOT NULL,
ProductID INTEGER NOT NULL,
FOREIGN KEY (ProductID) REFERENCES Product(ID),
FOREIGN KEY (CustomerID) REFERENCES Customer(ID),
CHECK (Status IN ('open','closed') AND Priority IN (1,2,3))
);
CREATE TABLE qUpdate
(
ID INTEGER NOT NULL PRIMARY KEY,
Message VARCHAR(1000) NOT NULL,
UpdateTime TIMESTAMP NOT NULL,
QuestionID INTEGER NOT NULL,
StaffID INTEGER,
FOREIGN KEY (StaffID) REFERENCES Staff(ID),
FOREIGN KEY (QuestionID) REFERENCES Question(ID)
);
Some sample inserts:
INSERT INTO Customer (ID, Name, Email) VALUES (1, 'testname1', 'testemail1');
INSERT INTO Customer (ID, Name, Email) VALUES (2, 'testname2', 'testemail2');
INSERT INTO Staff (ID, Name) VALUES (1, 'Don Keigh');
INSERT INTO Product (ID, Name) VALUES (1, 'Xbox');
INSERT INTO Question (ID, Problem, Status, Priority, LoggedTime, CustomerID, ProductID)
VALUES (1, 'testproblem1', 'open', 3, '2012-04-14 09:30', 2, 1);
INSERT INTO Question (ID, Problem, Status, Priority, LoggedTime, CustomerID, ProductID)
VALUES (2, 'testproblem2', 'open', 3, '2012-04-14 09:30', 2, 1);
INSERT INTO qUpdate (ID, Message, UpdateTime, StaffID, QuestionID) VALUES (2, 'testmessage1','2012-07-12 14:27', 1, 1);
INSERT INTO qUpdate (ID, Message, UpdateTime, QuestionID) VALUES (3, 'testmessage1','2012-06-18 19:42', 2);
What I've done so far (which obviously doesn't work)
UPDATE Question
SET Status = 'closed'
WHERE EXISTS
(SELECT qUpdate.QuestionID
MAX(qUpdate.UpdateTime - Now() = INTERVAL '1 day') FROM qUpdate
LEFT JOIN Question ON qUpdate.QuestionID = Question.ID
WHERE qUpdate.StaffID IS NOT NULL);
I realise my explanation may be a bit confusing so if you need more info post and I'll reply ASAP
UPDATE Question
SET Status = 'closed'
where
-- this clause asserts there's at least one staff answer
exists (
select null from qUpdate
where qUpdate.QuestionID = Question.ID
and StaffID is not null
)
-- this clause asserts there's been no update in the last 24 hours
and not exists (
select null from qUpdate
where qUpdate.QuestionID = Question.ID
and qUpdate.UpdateTime > (now() - interval '24 hours')
)
and Status = 'open';
You'll almost certainly want an index on qUpdate(QuestionID) or possibly qUpdate(QuestionID,UpdateTime) or qUpdate(QuestionID,StaffID) to get good performance on the subselects in the exists() tests.