Create a relation - sql-server

I created the following tables and inserted few values.
create table books
(
bookid integer primary key,
booktitle varchar(20),
year integer,
publisherid integer foreign key references publisher(publisherid),
price integer,
number integer
)
create table publisher
(
publisherid integer primary key,
publishername varchar(20)
)
create table author
(
authorid integer primary key,
authorname varchar(20)
)
create table bookauthor
(
bookid integer foreign key references books(bookid),
authorid integer references author(authorid),
earnings integer
)
create table bookreference
(
bookid integer foreign key references books(bookid),
referencebook varchar(20),
times integer
)
create table reviewer
(
reviewerid integer primary key,
reviewername varchar(20)
)
create table bookreview
(
bookid integer foreign key references books(bookid),
reviewerid integer foreign key references reviewer(reviewerid),
score integer
)
Now, I want to solve following query in SQL Server 2000. Find all the books published in 2003 and reviewed by both ‘Sammer Tulpule’ and ‘Hemant Mahta’ . I am not getting any idea about query. How can I write it?
Thanks,
Pooja

SELECT b.bookid as bookid
FROM (books b INNER JOIN bookreview br ON (br.bookid=b.bookid))
INNER JOIN reviewer r ON (br.reviewerid = r.reviewerid)
WHERE year=2003 AND r.reviewername IN ('Sammer Tulpule', 'Hemant Mahta')
GROUP BY b.bookid
HAVING COUNT(r.reviewerid) >= 2
Obviously, this is assuming that you don't have duplicated entries in the bookreview table.

(SELECT b.* FROM books b, bookreview br, reviewer rv WHERE b.year = 2003 AND br.bookid = b.bookid and br.reviewerid = rv.reviewerid and rv.reviewername = 'Sammer Tulpule')
INTERSECT
(SELECT b.* FROM books b, bookreview br, reviewer rv WHERE b.year = 2003 AND br.bookid = b.bookid and br.reviewerid = rv.reviewerid and rv.reviewername = 'Hemant Mahta')
I am typing this on the fly so there might be small errors, but you get the general idea...

Related

I am trying fetch data from sqlit3 database and haveing this ambiguous column name problem, I don't see any issue, need an explanation

I am Trying to fetch the list of movies where two people are star in the same movies here is the table format:
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE stars (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE movies (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
PRIMARY KEY(id)
);
Query:
Running the below query is giving me ambiguous column name: movie_id, i don't understand what is the issue here,
select movie_id from (
(select movie_id,person_id from (
select id from people where name = "Johnny Depp") as x
inner join
stars on x.id = stars.person_id) as xx
inner join
(select movie_id,person_id from (
select id from people where name = "Helena Bonham Carter") as y
inner join
stars on y.id = stars.person_id) as yy
on xx.movie_id = yy.movie_id
);
It is easier to group by the movies and select only those groups having the 2 different names from the where clause
select s.movie_id
from people p
join stars s on p.id = s.person_id
where p.name in ('Johnny Depp', 'Helena Bonham Carter')
group by s.movie_id
having count(distinct p.name) = 2
The issue with your query is that when selecting from 2 tables with the same column name you have to tell the DB which column you want to use by adding the table name
select xx.movie_id ...

How create procedure to retrieve data from three tables with user inserted data

I have three tables DEPARTMENTS, SEMESTER and SUBJECTS
CREATE TABLE DEPARTMENTS
(
D_ID INT PRIMARY KEY identity(1, 1),
department_name VARCHAR(50) NOT NULL
);
CREATE TABLE SEMESTER
(
D_ID INT FOREIGN KEY REFERENCES departments(D_ID),
sem_id INT PRIMARY KEY identity(1, 1),
semester INT CHECK
(semester BETWEEN 1 AND 8) NOT NULL
);
CREATE TABLE SUBJECTS
(
D_ID INT FOREIGN KEY REFERENCES departments(D_ID),
sem_id INT FOREIGN KEY REFERENCES semester(sem_id),
sub_id INT PRIMARY KEY identity(1, 1),
sub_name VARCHAR(50),
syllabus VARBINARY(MAX),
exam_format VARBINARY(MAX)
);
If user insert department_name in DEPARTMENTS table and semester from semester table then how to get sub_name from subject
Firstly, your design is wrong. you don't need to have a reference from Subject to Departments because Department is accessible through Semester and the D_Id in Subject is redundant, IMHO.
Second, what is your criteria for retrieving subject?
Select Sub_Name
from Subjects as sbj
inner join Departments as dpt on sbj.D_Id = dpt.D_Id
inner join Semester as smt on sbj.sem_id = smt.sem_id
where (YOUR CRITERIA)
This is basically the general correlation among your tables,
so you could specify the conditions and list of fields to be retrieved.
However it's best to rectify your design first and then access department
through Semester :
inner join Departments as dpt on smt.D_Id = dpt.D_Id
thereby you're gonna use the dependency in semester rather than subjects!
p.s.
if the department in subject is different from the on in semester
then your layout is good as it is, but if they're same thing then
you can get rid of the latter
You need to use join here
select sub.sub_name,dep.department_name,sem.semester
from subjects sub
join departments dep on dep.d_id = sub.d_id
join semester sem on sem.sem_id = sub.sem_id;

Self Join on large tables slowness issue

I have two tables like...
table1 (cid, duedate, currency, value)
main_table1 (cid)
My query is like below, I am find out co-relation between each cid and table1 contains 3 million records(cid and duedate column is compositely unique) and main_table contains 1500 records all unique.
SELECT
b.cid, c.cid,
(COUNT(*) * SUM(b.value * c.value) -
SUM(b.value) * SUM(c.value)) /
(SQRT(COUNT(*) * SUM(b.value * b.value) -
SUM(b.value) * SUM(b.value)) *
SQRT(COUNT(*) * SUM(c.value * c.value) -
SUM(c.value) * SUM(c.value))
) AS correl_ij
FROM
main_table1 a
JOIN
table1 AS b ON a.cid = b.cid
JOIN
table1 AS c ON b.cid < c.cid
AND b.duedate = c.duedate
AND b.currency = c.currency
GROUP BY
b.cid, c.cid
Please suggest how to optimize this query because it is running slow.
CREATE TABLE #table1(
id int identity,
cid int NOT NULL,
duedate date NOT NULL,
currency char(3) NOT NULL,
value float,
PRIMARY KEY(id,currency,cid,duedate)
);
CREATE TABLE #main_table1(
cid int NOT NULL PRIMARY KEY,
currency char(3)
);
--#main table contains 155000 cid records there is no duplicate values
insert into #main_table1
values(19498,'ABC'),(19500,'ABC'),(19534,'ABC')
INSERT INTO #table1(CID,DUEDATE,currency,value)
VALUES(19498,'2016-12-08','USD',-0.0279702098021799) ,
(19498,'2016-12-12','USD',0.0151285161000268),
(19498,'2016-12-15','USD',-0.00965080868337728),
(19498,'2016-12-19','USD',0.00808331709091531)
There are 3 million records in this table for diffrent dates and cid and most of the cid are present in #main_table1.
I am using a.cid < b.cid to remove duplicate relationship between a.cid and b.cid beause i am deriving corelation between each cid.
so 19498 -->>19500 corelation is calculated hence then i do not want 19500--> 19498 because it would be same but duplicate.
That PK is silly. Why would you include Iden in a composite PK let alone in the first position? Drop Iden unless you have to have it for some misguided reason.
PRIMARY KEY(cid, currency, duedate)
Or the natural key if different
If you're commonly joining or sorting on the cid column, you probably want a clustered index on that column or a composite beginning with that column.
If cid, duedate is unique then you can consider removing the id altogether.
If you want to retain id for some reason, make it PRIMARY KEY NONCLUSTERED, and specify a clustered index on cid, duedate.

TSQL to insert a set of rows and dependent rows

I have 2 tables:
Order (with a identity order id field)
OrderItems (with a foreign key to order id)
In a stored proc, I have a list of orders that I need to duplicate. Is there a good way to do this in a stored proc without a cursor?
Edit:
This is on SQL Server 2008.
A sample spec for the table might be:
CREATE TABLE Order (
OrderID INT IDENTITY(1,1),
CustomerName VARCHAR(100),
CONSTRAINT PK_Order PRIMARY KEY (OrderID)
)
CREATE TABLE OrderItem (
OrderID INT,
LineNumber INT,
Price money,
Notes VARCHAR(100),
CONSTRAINT PK_OrderItem PRIMARY KEY (OrderID, LineNumber),
CONSTRAINT FK_OrderItem_Order FOREIGN KEY (OrderID) REFERENCES Order(OrderID)
)
The stored proc is passed a customerName of 'fred', so its trying to clone all orders where CustomerName = 'fred'.
To give a more concrete example:
Fred happens to have 2 orders:
Order 1 has line numbers 1,2,3
Order 2 has line numbers 1,2,4,6.
If the next identity in the table was 123, then I would want to create:
Order 123 with lines 1,2,3
Order 124 with lines 1,2,4,6
On SQL Server 2008 you can use MERGE and the OUTPUT clause to get the mappings between the original and cloned id values from the insert into Orders then join onto that to clone the OrderItems.
DECLARE #IdMappings TABLE(
New_OrderId INT,
Old_OrderId INT)
;WITH SourceOrders AS
(
SELECT *
FROM Orders
WHERE CustomerName = 'fred'
)
MERGE Orders AS T
USING SourceOrders AS S
ON 0 = 1
WHEN NOT MATCHED THEN
INSERT (CustomerName )
VALUES (CustomerName )
OUTPUT inserted.OrderId,
S.OrderId INTO #IdMappings;
INSERT INTO OrderItems
SELECT New_OrderId,
LineNumber,
Price,
Notes
FROM OrderItems OI
JOIN #IdMappings IDM
ON IDM.Old_OrderId = OI.OrderID

get max from table where sum required

Suppose I have a table with following data:
gameId difficultyLevel numberOfQuestions
--------------------------------------------
1 1 2
1 2 2
1 3 1
In this example the game is configured for 5 questions, but I'm looking for a SQL statement that will work for n number of questions.
What I need is a SQL statement that given a question, displayOrder will return the current difficulty level of question. For example - given a displayOrder of 3, with the table data above, will return 2.
Can anyone advise how the query should look like?
I'd recommend a game table with a 1:m relationship with a question table.
You shouldn't repeat columns in a table - it violates first normal form.
Something like this:
create table if not exists game
(
game_id bigint not null auto_increment,
name varchar(64),
description varchar(64),
primary key (game_id)
);
create table if not exists question
(
question_id bigint not null auto_increment,
text varchar(64),
difficulty int default 1,
game_id bigint,
primary key (question_id) ,
foreign key game_id references game(game_id)
);
select
game.game_id, name, description, question_id, text, difficulty
game left join question
on game.game_id = question.game_id
order by question_id;
things might be easier for you if you change your design as duffymo suggests, but if you must do it that way, here's a query that should do the trick.
SELECT MIN(difficultyLevel) as difficltyLevel
FROM
(
SELECT difficltyLevel, (SELECT sum(numberOfQuestions) FROM yourtable sub WHERE sub.difficultyLevel <= yt.difficultyLevel ) AS questionTotal
FROM yourTable yt
) AS innerSQL
WHERE innerSQL.questionTotal >= #displayOrder

Resources