Optimal SQLite Query Remove Duplicates - sql

I have two tables with the following setup:
category: (id, name)
item: (id, name, category_id) - category_id is foreign key to category table
Now I am writing a query to retrieve a subset from the category table of only used categories:
SELECT c.id, c.name
FROM category c
WHERE c.id IN (SELECT DISTINCT category_id FROM item)
The above query works fine. I'm just wondering if this is the most optimal way of doing the query or if there's something else that I could do via a join or something

Transforming the IN (SELECT) to EXISTS (SELECT ... WHERE ) might help:
SELECT c.id, c.name
FROM category c
WHERE EXISTS (SELECT 1 FROM item WHERE item.category_id = c.id)
Another possibility (I expect it to be slower, but it always depends on your db):
SELECT c.id, c.name
FROM category c
INNER JOIN item ON item.category_id = c.id
GROUP BY c.id
Or you could use DISTINCT instead of GROUP BY:
SELECT DISTINCT c.id, c.name
FROM category c
INNER JOIN item ON item.category_id = c.id
And if speed is that important, don't forget to call ANALYZE from time to time:
http://www.sqlite.org/lang_analyze.html
Some other variants for fun:
SELECT c.id, c.name
FROM category c
INNER JOIN (SELECT DISTINCT item.category_id ) AS i_c ON i_c.category_id = c.id
Another:
SELECT c.id, c.name
FROM category c
EXCEPT
SELECT c.id, c.name
FROM category c
LEFT JOIN item ON item.category_id = c.id
WHERE item.category_id IS NULL

Use Join:
SELECT c.id, c.name
FROM category c
JOIN item i on c.id=i.category_id
GROUP BY c.id, c.name

Related

How to group within categories

I'm trying to eliminate duplicated data within ID across multiple categories. Is it possible to eliminate duplicates for each category in one query? If I had one category that would be simple as adding a group by ID.
INSERT INTO TABLE_PROFILES(CATEGORY,ID,REGION_ID)
SELECT D.category_id, C.ID
FROM MATCH_DATA C JOIN
CATEGORY_TABLE D
ON c.EXTERNAL_ID = d.device_id;
Try using distinct
INSERT INTO TABLE_PROFILES(CATEGORY,ID,REGION_ID)
SELECT distinct D.category_id, C.ID
FROM MATCH_DATA C JOIN
CATEGORY_TABLE D
ON c.EXTERNAL_ID = d.device_id;
Is that what you were looking for? Distinct?
Insert INTO TABLE_PROFILES(CATEGORY,ID) SELECT distinct D.category_id, C.ID FROM MATCH_DATA C JOIN CATEGORY_TABLE D ON c.EXTERNAL_ID = d.device_id;

How to express nested SELECTS as a simpler JOIN

I have three tables:
customers: id, name
transactions: id_customer, id_item
items: id, name
I want to write a simple query that will return the names of all customers who ordered an item with the name 'apple'
I know I can do something like:
SELECT
name
FROM
customers
WHERE id IN (SELECT
id_customer
FROM
transactions
WHERE id_item IN (SELECT
id
FROM
items
WHERE name = 'apple'))
This may not be too bad, but it seems convoluted and like there might be a more straightforward way to do this with a join?
Yes, it's definitely better to use a join in this case. Try this:
SELECT DISTINCT
A.name
FROM
customers A INNER JOIN
transactions B ON A.id = B.id_customer INNER JOIN
items C ON B.id_item = C.id
WHERE C.name = 'apple'
In this way, only customers who placed an order containing 'apple's will be shown.
Use Join case, this a best way to approach.
Select C.name from Customers C
Inner Join Transaction T on C.id = T.id_customer
Inner Join Items I on T.id_items = I.id
where I.name = "Apple"
Above query will return the names of all customers who ordered "Apple".

SQL Server recursive CTE query to find all items belonging to all categories and their descendants

I am a bit confused trying to use a recursive CTE to list all the items in a product catalogue for all the categories and their respective parent categories that each item belongs to.
The tables are really simple...
Table Category; Columns: Id, ParentID, Title
Table itemCategory; Columns: ItemID, CategoryID
I am just struggling to work out how to get the results I want. My best attempt is not right:
WITH
CTE (itemID, categoryID, title) AS (
SELECT itemID, categoryID, title
FROM itemcategory
INNER JOIN category ON category.ID = itemcategory.categoryID
UNION ALL
SELECT iI.ItemID, iI.categoryID, i.title
FROM itemcategory iI INNER JOIN category i ON i.ID = iI.categoryID
INNER JOIN CTE ON CTE.categoryID = i.ParentID)
SELECT * FROM CTE
I have a similar query which counts the number of items under each category:
WITH cte_count_category(id, parentid, c)
AS (SELECT c1.id,
c1.parentid,
(SELECT Count(*)
FROM (SELECT DISTINCT itemid
FROM itemcategory AS iI
WHERE iI.categoryid = c1.id) AS t1) AS c
FROM category AS c1
UNION ALL
SELECT c2.id,
c2.parentid,
d.c
FROM category c2
INNER JOIN cte_count_category d
ON c2.id = d.parentid)
SELECT cte_count_category.id,
cte_count_category.parentid,
title,
Sum(c) itemCount
FROM cte_count_category
LEFT JOIN category
ON category.id = cte_count_category.id
GROUP BY cte_count_category.id,
cte_count_category.parentid,
title
HAVING Sum(c) > 0
ORDER BY itemcount DESC;
I just can't work out how to get it to list all the items. Any help would be appreciated.
Edit: The categories need to be n deep although it is unlikely that it will go beyond 4 levels most of the time.
The output I am looking for is one row per itemID per category it falls under so, an item in the 'music book' category would appear under both the 'music book' category and the 'book' category.
I think the logic you need is confused by the items. You don't actually need them in the CTE (you can join them in afterwards). Alternatively, you can put them in the "base" part of the construct. Only the categories are needed in the recursive part.
This may be what you are looking for:
WITH CTE (itemID, categoryID, title, parentid, lev) AS (
SELECT ic.itemID, c.categoryID, c.title, c.parentid, 0
FROM itemcategory ic INNER JOIN
category c
ON c.ID = ic.categoryID
UNION ALL
SELECT cte.ItemID, c.categoryID, c.title, c.parentid, cte.lev + 1
FROM CTE INNER JOIN
category c
ON CTE.ParentID = c.categoryID
)
SELECT * FROM CTE

SQL - Count on recursive category

I'm stucked into a count query.
I have 3 tables:
Articoli
ID | Title | ecc...
Categories
ID | Name | Parent
articles_category
category_id | article_id
Category are recursive, for example I have a main category "News" with 3 sub cat.
I need to count how many article are in "News", but my article are tagged in the "articles_category" table with the subcat ID (if there is a SubCat) or with main Cat ID, if it have no subcat. So far i tried:
SELECT count(a.id), child.name AS child, parent.name AS parent
FROM categories parent
JOIN categories child ON child.parent = parent.tid
JOIN categories_articoli ca ON child.tid = ca.category_id
OR parent.tid = ca.category_id
JOIN articoli a ON a.id = ca.articolo_id
GROUP BY parent.tid
But this return me only the parent cat that have subcategory, but this is everytime true. Any suggestion?
You need to use recursive sql on Categories table.
Try this:
Count(*) of articles in News category:
with category_tree(id) as
(select c.id
from Categories c
where c.name='News'--category tree starting with 'News'
union all
select c.id
from category_tree ct
inner join Categories c
on c.parent = ct.id)
select count(distinct ac.article_id) from category_tree ct
inner join articles_category ac on ac.category_id = ct.id
Count(*) of articles by category:
with category_tree(id, root_category_name) as
(select c.id, c.name
from Categories c
where c.parent is null
union all
select c.id, ct.root_category_name
from category_tree ct
inner join Categories c
on c.parent = ct.id)
select ct.root_category_name, count(distinct ac.article_id) from category_tree ct
inner join articles_category ac on ac.category_id = ct.id
group by ct.root_category_name
http://sqlfiddle.com/#!4/d42aa/12
Thanks a lot!
Unlucky I can't use the "WITH" statement in mysql (sorry I didn't specify this), but i solve my issue in this way:
create a dataset in wich every ID is associated with his parent_category name
join it on the categories_articoli table
group by parent_category name.
Here is the query if someone may need something like this:
SELECT count(distinct ca.articolo_id), cat.name
FROM categories_articoli ca
JOIN(
SELECT c.tid AS ID, c.name AS name, c.parent
FROM categories c
WHERE c.parent = 0 AND c.tid <> 6
UNION
SELECT c.tid AS ID, parent.name AS name, c.parent
FROM categories c
JOIN categories parent ON c.parent = parent.tid
WHERE c.parent <> 0 AND c.tid <> 10
) cat
ON cat.ID = ca.category_id
GROUP BY cat.name
i think that it is wrong, becouse your solution dont write "top" categories (fe.: you have cat number 3 in 2 in 1 and items only in category 3 - your solution will return right count of items in category 3 and 2, but category 1 wont be in result and it should be there)

Pulling my hair with this Syntax Error

I seem to be running into a query syntax error but cannot seem to isolate it. I am using MS Access and when I run the queries I get a syntax error in FROM clause.
I have two tables and they are in a one to many relationship:
Table 1 called (customer) with the following fields:
ID
FirstName
Table 2 called (tblservice) with the following fields:
serviceID
Timing
Total
customerID <-Foreign Key
First Query:
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
group by s.id
Second Query(30 day span):
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by s.id
Try this:
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
group by c.id, c.firstname
and
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by c.id, c.firstname
You cannot select c.id and c.firstname unless you group by them. And you can use count(c.id) since you are already grouping by c.id. I have not used SQL in MS Access though. So I am not 100% sure. Try it out.
MS Access may require you to use INNER JOIN instead of just JOIN.