Sql Query for number of available and total books - sql

I need a query which will return list of book from book table
but it will show total number of books and books available too ..
And for number of books it will count number of books with simular ISBN n amount count which will count number of books with simular ISBN with status available
Table structure
Bookid booktitle author ISBN edition publisher price supplier volume language status
status contains available,issued
I want result as
BookTitle Author Publisher Location Total available
Please also explain the query because I need to implement it on many tables
Queries i tried this far
select *,count(1) as Quantity from Book group by Book.BookTitle
It successfully gives me book list with total quantity
and
select *,count(1) as Quantity from Book where status='Available' group by Book.BookTitle
It Successfully gives me list of books with how many are available.
but i need combination of both total and available ..

In brevi:
select isbn, booktitle, sum(case when status='available' then 1 else 0 end) as avail, count(*) as total
from Book
group by isbn, booktitle
http://sqlfiddle.com/#!2/80a41/5
i definitely suggest you to read about 3rd normal form

You must group by all items in the SELECT query. You can either count(*) or sum(1). Here is how I would do:
SELECT BookId, Name, Available, count(*) as Quantity
FROM Book GROUP BY Bookid,Name,Available

select d.* from (select BookTitle,Author,Publisher,Location,count(BookTitle) as Total, Status, Count(Status) as StatusQuantity from Book group by BookTitle,Author,Publisher,Location,Status)d where d.Status='Available'

Try this one:
SELECT
BookTitle,
Author,
Publisher,
Location,
SUM(CASE WHEN status='available' THEN 1 ELSE 0 END) Available,
COUNT(*) AS Total
FROM Book
GROUP BY BookTitle,Author,Publisher,Location

Are you have any key constraints??
you must split the table..
Using the bookid or ISBN as a primary key,store the status in a separate table..
it will be easy for further process otherwise the records should be redundant..

Related

How to query if a customer has gave a lower rating before?

product(prod id, product name, product price, product manufacturer)
customer(cust id, customer name, customer address)
rating(prod id, cust id, rating date, rating stars)
Foreign Key: prod id references prod id in product table
Foreign Key: cust id references cust id in customer table
sale(sale id, prod id, cust id, sale quantity, sale cost)
Foreign Key: prod id references prod id in product table
Foreign Key: cust id references cust id in customer table
These schemas represent product information, including sales. Customers can provide ratings for products in a 0 to 5 stars fashion. It is possible for clients to rate the same product more than once.
And here is the question:
For all cases in which the same customer rated the same product more
than once, and in some point in time gave it a lower rating than
before, return the customer name, the name of the product, and the
lowest star rating that was given.
Is there a way to query customers that gave a lower rating before? thank you
This is a rather tricky question. But the answer to your question is EXISTS. The following returns the pairs that have this behavior:
select r.cust_id, r.prod_id
from ratings r
where exists (select 1
from ratings r2
where r2.cust_id = r.cust_id and
r2.prod_id = r.prod_id and
r2.date < r.date and
r2.stars < r.stars
)
Alas, this does not return the minimum stars (because nothing will be before it). One trick is to invert the logic -- so select any rating that has a higher rating following it. The minimum will meet this criterion, so you can do:
select r.cust_id, r.prod_id, min(r.stars)
from ratings r
where exists (select 1
from ratings r2
where r2.cust_id = r.cust_id and
r2.prod_id = r.prod_id and
r2.date > r.date and
r2.stars > r.stars
)
group by r.stars;
I'll let you add the JOINs to get the names.

How to make SQL request to get table of counted keywords?

I need to make a single SQL request like this:
SELECT genres, count(*) FROM books WHERE genres LIKE 'Fiction%'
But I need to use many keyword like 'Nonfiction%', 'Historical' ect. The output should be a table:
Fiction 8654
Nonfiction 6543
Historical 2344
What SQL request I have to use to get this result?
Hopefully, you can just do:
SELECT genres, count(*)
FROM books
GROUP BY genres;
In fact, you could just do this if you had a table called BookGenres with one row per book and per genre. That is the right way to store this data.
In this case you appear to be looking only for the first genre in the list. You can use case:
select (case when genres like 'Fiction%' then 'Fiction'
when genres like 'Nonfiction%' then 'Nonfiction'
when genres like 'Historical%' then 'Historical'
else 'Other'
end) as genre, count(*)
from books
group by (case when genres like 'Fiction%' then 'Fiction'
when genres like 'Nonfiction%' then 'Nonfiction'
when genres like 'Historical%' then 'Historical'
else 'Other'
end);
You could use group by
SELECT genres, count(*)
FROM books
GROUP BY genres
or for a set of values
SELECT genres, count(*)
FROM books
WHERE genres in ( 'Fiction', 'Nonfiction', 'Historical')
GROUP BY genres

Oracle SQL - Counting distinct column combinations

Running Oracle 12.1. I have a Line Items table. Its structure is fixed, and I cannot change it. I need to build a dashboard style page of information of the Line items table for a person to look at their sales territory. This person might be a GVP, who owns a large territory, or a Manager, or an individual rep. The Line Items table is pretty de-normalized, as this copy is part of a DW. This ‘copy’ of the table is only updated every 2 weeks, and it looks like this.
Line_Item_ID // PK
Account_ID //
Company_Name // The legal name of the Headquarters
LOB_Name // Line of business, aka Division within the Company_Name
Account_Type // One of 2 values, ‘NAMED’ or “GENERAL’
ADG_STATUS // 3 possible values, ‘A’, ‘D’ or ‘G’
Industry // One of 15 values, for this example assume it is ONLY ‘MFG’, ‘GOV’, ‘HEALTHCARE’
// Now have the sales hierarchy of the rep who sold this
GVP // Group Vice President
SVP // Sales Vice President
RVP // Regional Vice President
RM // Regional Manager
REP // Sales Rep
// Now have information about the product sold
ProductName
ProductPrice
VariousOtherFields….
I need to make an aggregated table which will be used for quick access of the dashboard. It will have counts of various combinations, and there will be one row per PERSON, not account. A person is every UNIQUE person listed in any of the GVP, SVP, RVP, RM or REP fields. Here is what the end result table will look like. Other than PERSON, every column is based on a DISTINCT count, and it is an integer value.
PERSON
TOTAL_COMPANIES // For this person, count of DISTINCT COMPANY_NAME
TOTAL_LOBS // For this person, count of DISTINCT LOBS
TOTAL_COMPANIES_NAMED // count of DISTINCT COMPANY_NAME with ACCOUNT_TYPE=’NAMED’
TOTAL_COMPANIES_GENERAL // count of DISTINCT COMPANY_NAME with ACCOUNT_TYPE=’GENERAL’
TOTAL_LOBS_NAMED // count of DISTINCT LOB_NAME with ACCOUNT_TYPE=’NAMED’
TOTAL_LOBS_GENERAL // count of DISTINCT LOB_NAME with ACCOUNT_TYPE=’GENERAL’
TOTAL_COMPANIES_STATUS_A // count of DISTINCT COMPANY_NAME with ADG_STATUS=’A’
TOTAL_COMPANIES_STATUS_D // count of DISTINCT COMPANY_NAME with ADG_STATUS=’D’
TOTAL_COMPANIES_STATUS_G // count of DISTINCT COMPANY_NAME with ADG_STATUS=’G’
TOTAL_LOB_STATUS_A // count of DISTINCT LOB_NAME with ADG_STATUS=’A’
TOTAL_LOB_STATUS_D // count of DISTINCT LOB_NAME with ADG_STATUS=’D’
TOTAL_LOB_STATUS_G // count of DISTINCT LOB_NAME with ADG_STATUS=’G’
//Now Various Industry Permutations. I have 15 different industries, but only showing 2. This will only be at the COMPANY_NAME level, not the LOB_NAME level
MFG_COMPANIES_STATUS_A // count of DISTINCT COMPANY_NAME with ADG_STATUS=’A’ and Industry = ‘MFG’
MFG_COMPANIES_STATUS_D // count of DISTINCT COMPANY_NAME with ADG_STATUS=’D’ and Industry = ‘MFG’
MFG_COMPANIES_STATUS_G // count of DISTINCT COMPANY_NAME with ADG_STATUS=’G’ and Industry = ‘MFG’
GOV_COMPANIES_STATUS_A // count of DISTINCT COMPANY_NAME with ADG_STATUS=’A’ and Industry = ‘GOV’
GOV_COMPANIES_STATUS_D // count of DISTINCT COMPANY_NAME with ADG_STATUS=’D’ and Industry = ‘GOV’
GOV_COMPANIES_STATUS_G // count of DISTINCT COMPANY_NAME with ADG_STATUS=’G’ and Industry = ‘GOV’
There are approx. 400 people, 35000 unique accounts, and 200,000 entries in the line items table.
So what is my strategy? I have thought about making another table of unique PERSON values, and using it as a driving table. Let’s call this table PERSON_LIST.
Pseudo-code…
For each entry in PERSON_LIST
For all LINE_ITEMS where person_list in ANY(GVP, SVP, RVP, RM, REP) do
Calculations…
This would be an incredibly long running process…
How can I do this more effectively (set based as opposed to row by row)? I believe I would have to use the PIVOT operator for the INDUSTRY list, but can I use PIVOT with additional criteria? Aka count of distinct COMPANY with a specific industry and a specific ADG_STATUS?
Any ideas or SQL code most appreciated.
You could unpivot the original data to get the data from the original GVP etc. columns into one 'person' column:
select * from line_items
unpivot (person for role in (gvp as 'GVP', svp as 'SVP', rvp as 'RVP',
rm as 'RM', rep as 'REP'))
And then use that as a CTE or inline view, with pretty much what you showed; conditional aggregation using case expressions, something like:
select person,
count(distinct company_name) as total_companies,
count(distinct lob_name) as total_lobs,
count(distinct case when account_type='NAMED' then company_name end)
as total_companies_named,
count(distinct case when account_type='GENERAL' then company_name end)
as total_companies_general,
count(distinct case when account_type='NAMED' then lob_name end)
as total_lobs_named,
count(distinct case when account_type='GENERAL' then lob_name end)
as total_lobs_general,
count(distinct case when adg_status='A' then company_name end)
as total_companies_status_a,
count(distinct case when adg_status='D' then company_name end)
as total_companies_status_d,
count(distinct case when adg_status='G' then company_name end)
as total_companies_status_g,
count(distinct case when adg_status='A' then lob_name end)
as total_lob_status_a,
count(distinct case when adg_status='D' then lob_name end)
as total_lob_status_d,
count(distinct case when adg_status='G' then lob_name end)
as total_lob_status_g,
count(distinct case when adg_status='A' and industry = 'MFG' then company_name end)
as mfg_companies_status_a,
count(distinct case when adg_status='D' and industry = 'MFG' then company_name end)
as mfg_companies_status_d,
count(distinct case when adg_status='G' and industry = 'MFG' then company_name end)
as mfg_companies_status_g,
count(distinct case when adg_status='A' and industry = 'GOV' then company_name end)
as gov_companies_status_a,
count(distinct case when adg_status='D' and industry = 'GOV' then company_name end)
as gov_companies_status_d,
count(distinct case when adg_status='G' and industry = 'GOV' then company_name end)
as gov_companies_status_g
from (
select * from line_items
unpivot (person for role in (gvp as 'GVP', svp as 'SVP', rvp as 'RVP',
rm as 'RM', rep as 'REP'))
)
group by person;

Having problems identifying my mistake

The tables which are already created and unmodifiable are Book and Author.
Book (Title, Price, Yeareleased)
Author(AName,btitle,position)
Italized are the keys
and Btitle in Author is a foreign key that references Book(Title).
My SQL query:
select distinct AName
from Author
where position in (2,3) AND position<>1
group by AName
When I run this I get all the authors that have a book in position 2 or 3. Which is what I want but I'm only trying to get those authors which have a position 2 or 3 for all there books.
Essentially returning every author who was in the 2nd or 3rd position in all the books.
Maybe something like this would work:
select distinct AName
from #Author
where position in (2,3)
except
select distinct AName
from #Author
where position not in (2,3)
It makes a set of those authors who are in position 2 and 3 and then removes the ones who has another position.
It is not entirely clear whether someone who co-wrote 2 books and was listed second on one and third on the other should be selected or not. It is simpler to allow it; you can refine the query if you need the more stringent condition.
One way to answer this query makes the key observation that you're interested in authors for whom the count of the books they have written is equal to the count of the books where they are listed as second or third author.
Go for some TDQD — Test-Driven Query Design
Number of books each author wrote
SELECT Aname, COUNT(*) AS BookCount
FROM Author
GROUP BY AName
Number of books each author wrote as second or third author
SELECT Aname, COUNT(*) AS NonLeadAuthorCount
FROM Author
WHERE Position IN (2, 3)
GROUP BY Aname
Join those two where the counts are identical
SELECT X.Aname
FROM (SELECT Aname, COUNT(*) AS BookCount
FROM Author
GROUP BY AName
) AS X
JOIN (SELECT Aname, COUNT(*) AS NonLeadAuthorCount
FROM Author
WHERE Position IN (2, 3)
GROUP BY Aname
) AS Y
ON X.BookCount = Y.NonLeadAuthorCount
An alternative way of looking at is 'the set authors who have written a book in position 2 or 3 minus the set of authors who have written a book where the position is neither 2 nor 3'. For this, see the answer by jpw.
Trying to write standard SQL:
SELECT AName FROM (
SELECT
AName,
COUNT(*) AS count_all,
(SELECT COUNT(*) FROM Author AS aa WHERE aa.AName = a.AName AND position=2) AS count_2,
(SELECT COUNT(*) FROM Author AS aa WHERE aa.AName = a.AName AND position=3) AS count_3,
FROM Author AS a
GROUP BY AName
) AS t
WHERE count_all = count_2
OR count_all = count_3
I hope this work for you.
Try this:
select AName from Author where position=2 OR position=3 group by AName;
Try adding
and AName not in (select AName from Author where position != 2 and position != 3
Or something like that...

sub query returned more than 1 value

I have to write a query which returns the price of books greater than the average price for books of similar type. So, I did this using the below query:
select title
from titles
where price >
(
select avg(price)
from titles
group by type
)
However it throws this error:
subquery returned more than 1 value
It is understandable that > cannot be used for a list of values. But I wanted to know the way I can solve this. Please let me know the query I need to use. The DB is pubs
titles table:
title_id , title, type, price, advance, notes, sales
so I need to get the title with price > average (price) of similar types
The key is to use the type from the main query to filter the type in the subquery.
SELECT t.title
FROM titles t
WHERE t.price > (SELECT AVG(t2.price)
FROM titles t2
WHERE t2.type = t.type)
You have more than one type, so each type is returning an average.
You can look into the SOME|ANY or ALL operators to look at the list of items
If you were looking for the titles whose prices are higher than all of the average types, An example of this would be:
SELECT title
FROM titles
WHERE price > ALL
(
SELECT AVG(price)
FROM titles
GROUP BY type
)
You could similarly change ALL to SOME or ANY to suit your needs.
select title
from titles
where price >
(select avg(price) from titles)
The above query assumes that you want the average of all title prices to compare against. This will return a scalar value and allow your query to succeed.
You need to link types from subquery and main query, example:
select
a.title
from
titles a
where
a.price > ( select avg(b.price) from titles b where a.type = b.type group by b.type)