Text search of a many-to-many data relation - sql

I know this must have been answered before here, but I simply can't find a matching question.
Using a LIKE '%keyword%', I want to search a many-to-many data relationship in a MSSQL database and reduce it to a one-to-one result set. The two tables are joined through a linking table. Here's a very simplified version of what I'm talking about:
Books:
book_ id title
1 Treasure Island
2 Poe Collected Stories
3 Invest in Treasure Islands
Categories:
category_id name
1 Children
2 Adventure
3 Horror
4 Classic
5 Money
BookCategory:
book_id category_id
1 1
1 2
1 4
2 3
2 4
3 5
What I want to do is search for a phrase in the title (e.g. '%treasure island%') and get matching Books records that contain the search string and the single highest matching Categories record that goes with each book -- I want to discard the lesser category records. In other words, I'm looking for this:
book_id title category_id name
1 Treasure Island 4 Classic
3 Invest in Treasure Islands 5 Money
Any suggestions?

Try this. Filter your lookup table, then join:
With maxCategories AS
(select book_id, max(category_id) as category_id from BookCategory group by book_id)
select Books.book_id, Books.Title, Categories.category_id, Categories.name
from Books
inner join maxCategories on (Books.book_id = maxCategories.book_id)
inner join Categories on (Categories.category_id = maxCategories.category_id)
where Books.title like '%treasure island%'

Try:
select * from
(select b.*,
c.*,
row_number() over (partition by bc.book_id
order by bc.category_id desc) rn
from Books b
join BookCategory bc on b.book_id = bc.book_id
join Categories c on bc.category_id = c.category_id
where b.name like '%treasure island%') sq
where rn=1

Related

SQL Selecting & Counting In the same query

thanks in advance for any help on this, I am a bit of a newbie to MS SQL and I want to do something that I think is achievable but don't have the know how.
I have a simple table called "suppliers" where I can do (SELECT id, name FROM suppliers ORDER BY id ASC)
id
name
1
ACME
2
First Stop Business Supplies
3
All in One Supply Warehouse
4
Farm First Supplies
I have another table called "products"
id
name
supplier_id
1
Item 1
2
2
Item 2
1
3
Item 3
1
4
Item 4
3
5
Item 5
2
I want to list all the suppliers and get the total amount of products for each supplier if that makes sense on the same row? I am just not sure how to pass the suppliers.id through the query to get the count.
I am hoping to get to this:
id
name
total_products
1
ACME
2
2
First Stop Business Supplies
2
3
All in One Supply Warehouse
1
4
Farm First Supplies
0
I really appreciate any help on this.
Three concepts to grasp here. Left Join, group by, and Count().
select s.id, s.name, Count(*) as total_products
from suppliers s
left join products p on s.id=p.supplier_id --the left join gets your no matches
group by s.id, s.name
left join is a join where all of the values from the first table are kept even if there are no matches in the second.
Group by is an aggregation tool where the columns to be aggregated are entered.
Count() is simply a count of transactions for the grouped columns.
Try this :-
SELECT id, name, C.total_products
FROM Suppliers S
OUTER APPLY (
SELECT Count(id) AS total_products
FROM Products P
WHERE P.supplier_id = S.id
) C

Counting and Joining in SQL (oracle)

My database contains 2 tables called products and product_categories.
Product_categories contain category_id and category_name
Products contain category_id and other irrelevant info for this question.
I need to count the number of products using category_id from the products table and also display their name which is in the product_categories table.
Select * FROM product_categories
displays :
category_id, category_name
1 CPU
2 Video Card
3 RAM
4 Mother Board
5 Storage
And
Select * FROM products
displays (condensed):
category_id
399.77 564.89 1
481.56 554.99 1
4058.99 5499.99 2
3619.14 4139 2
2505.04 3254.99 2
... UPTO CATEGORY_ID 5
Current statement :
SELECT category_id , COUNT (1) AS "TOTAL"
FROM products
GROUP BY category_id;
Output:
Category_id, total
1 70
2 50
5 108
4 60
DESIRED RESULT: I need to display the category id followed by category name and finally the total number of products.
Join those tables:
select p.category_id,
c.category_name,
count(*) as total
from products p join product_categories c on c.category_id = p.category_id
group by p.category_id,
c.category_name
You might want to turn it to outer join if you want to display categories that don't exist in the products table.

Given two related tables, how to determine the most common relationships?

given a 3 tables: users, books, book_users, how would I determine what are the commons books?
users: id, first_name, last_name
books: id, name
books_users: book_id, user_id
Designer Output, something like:
book | count
radBookName | 22
SemiRad | 22
Thanks
You seems want simple JOIN with GROUP BY clause :
SELECT b.name, count(*) as user_count
FROM books b INNER JOIN
books_users bu
ON bu.book_id = b.id
GROUP BY b.name;
This would produce duplicate count if one book has same user, if you want unique count then use count(distinct bu.user_id) instead.

How to find all the pairs of tuples that agree on a certain attribute

I am trying to write a query in db2 for a database that has books and the customers who bought them and I am to find the pairs of customers who bought common books.
Say for example the DB is called "DB" and it looks like this
CustomerID Book Cost
1 Harry Potter 12
2 SOUE 6
3 Harry Potter 12
4 Harry Potter 12
5 SOUE 6
6 SOUE 6
I am basically trying to get the resulting table look like
Customer1 Customer2
1 3
1 4
2 5
2 6
I have tried using group by's but I cant seem to get the idea right
I've tried
Select book
from DB
group by book
which uniquely gives me all the books but I don't know how I would go about getting the customer pairs. Any help would be greatly appreciated thank you.
I'd self-join according to the book column. In order to avoid conceptual duplicates (e.g., 1-3 and 3-1), you could make an arbitrary decision to always display the lower customer ID on the left:
SELECT DISTINCT a.customerid, b.customerid
FROM mytable a
JOIN mytable b ON a.book = b.book AND a.customerid < b.customerid
EDIT:
To answer the question in the comments, if you want to display customer names instead of ids, you'd need to join the customers table to this query, twice, once for each column:
SELECT DISTINCT ca.name AS customer1, cb.name AS customer2
FROM purchases pa
JOIN purchases pb ON pa.book = pb.book AND pa.customerid < pb.customerid
JOIN customers ca ON pa.customer_id = ca.id
JOIN customers cb ON pb.customer_id = cb.id

T SQL Adress Table with the same Company need latest Contact

i got an Address Table with Primary and Secondary Company locations, example:
ADDRESSES:
ID CompanyName AdressType MainID Location
1 ExampleCompany H 0 Germany
2 ExampleCompany N 1 Sweden
3 ExampleCompany N 1 Germany
and we got another Contacts Table including the latest Contact to each of the Company Locations
Contacts
ID SuperID Datecreate Notes
1 1 10.04.2018 XY
2 3 09.04.2018 YX
3 2 11.04.2018 XX
Now we want to select the latest Contact per Company and sort them so we got a list of all our customers that we did not contact in a long time.
i thought about something like this:
SELECT
ADDRH.ID,
ADDRH.COMPANY1,
TOPCONT.ID,
TOPCONT.DATECREATE,
TOPCONT.NOTES0
FROM dbo.ADDRESSES ADDRH
OUTER APPLY (SELECT TOP 1 ID, SUPERID, DATECREATE, CREATEDBY, NOTES0 FROM DBO.CONTACTS CONT WHERE ADDRH.ID = CONT.SUPERID ORDER BY DATECREATE DESC) TOPCONT
WHERE
TOPCONT.ID IS NOT NULL
ORDER BY TOPCONT.DATECREATE
But this is still missing the fact that we got the same company multiple times in the addresses table. how can i create a list that got each company with the latest contact?
Thanks for your help
Greetings
Well, you have to remove duplicates from address as well. Because of the structure of your data, I think the best approach is to use row_number():
SELECT ac.*
FROM (SELECT a.ID, a.COMPANY1, c.ID, c.DATECREATE, c.NOTES0,
ROW_NUMBER() OVER (PARTITION BY a.COMPANY1 ORDER BY c.DATECREATE DESC) as seqnum
FROM dbo.ADDRESSES a JOIN
DBO.CONTACTS c
ON a.ID = c.SUPERID
WHERE c.ID IS NOT NULL
) ac
WHERE seqnum = 1
ORDER BY c.DATECREATE;