I can't get my desired SQL result - sql

I have two tables:
Owners
+----+------+------------+
| id | name | birth_year |
+----+------+------------+
| 1 | John | 1970 |
| 2 | Jane | 1980 |
| 3 | Jack | 1990 |
| 4 | Josh | 2000 |
+----+------+------------+
Buylog
+----+----------+------------+
| id | owner_id | date |
+----+----------+------------+
| 1 | 1 | 01/01/2016 |
| 2 | 2 | 01/02/2016 |
| 3 | 2 | 01/03/2016 |
| 4 | 1 | 01/04/2016 |
+----+----------+------------+
I need to get all the info from Owners table plus the count of buys per owner:
+-----------+-------------+-------------------+--------------+
| owners.id | owners.name | owners.birth_year | buylog.Count |
+-----------+-------------+-------------------+--------------+
| 1 | John | 1970 | 2 |
| 2 | Jane | 1980 | 2 |
| 3 | Jack | 1990 | 0 |
| 4 | Josh | 2000 | 0 |
+-----------+-------------+-------------------+--------------+
I tried the below query, but that returns with error:
Select
o.id,
o.name,
o.birth_year,
Count(b.id) as Count
From
owners o
Left Outer Join
buylog b
On
b.owner_id = o.id

The error message should be pretty clear, you are missing a group by clause:
Select
o.id,
o.name,
o.birth_year,
Count(b.id) as Count
From
owners o
Left Outer Join
buylog b
On
b.owner_id = o.id
Group By o.id,
o.name,
o.birth_year

Query by HoneyBadger will do just fine, however this might perform better:
SELECT o.id
, o.name
, o.birth_year
, COALESCE(b.Count, 0) AS Count
FROM owners o
LEFT JOIN (
SELECT owner_id, COUNT(*) AS Count
FROM buylog
GROUP BY owner_id
) AS b
ON b.owner_id = o.id;
It should bring exactly the same result.

SELECT o.*,
CASE
WHEN temp.Buylog_count IS NULL THEN 0
ELSE temp.Buylog_count
END
FROM Owners o
LEFT JOIN
(
SELECT b.owner_id AS oid, COUNT(*) AS Buylog_count
FROM Buylog b
GROUP BY b.owner_id
)temp ON temp.oid = o.id

Related

Limit join with pagination

These are my db tables:
Users
| id | nme |
|----|------|
| 1 | Adam |
| 2 | Bob |
| 3 | Jan |
| 4 | Nico |
Products
| id | price |
|----|-------|
| 1 | 500 |
| 2 | 700 |
| 3 | 900 |
Orders
| id | user_id | product_id |
|----|---------|------------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 7 | 3 | 1 |
| 8 | 3 | 2 |
| 9 | 3 | 3 |
| 10 | 4 | 3 |
I want to get up to 2 users, and their products bought. I came with this:
SELECT
users.id AS 'user_id', products.id AS 'product_id'
FROM
users
INNER JOIN
orders ON orders.user_id = users.id
INNER JOIN
products ON products.id = orders.product_id
ORDER BY
orders.id
OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY
But this returns:
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
What I want to get is up to 2 users, not orders. I want to get this:
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Any ideas?
I think you don't need a JOIN among tables but only using [Orders] table along with a window function such as DENSE_RANK() as seems the most suitable to filter out to return the result set such as
;WITH cte AS
(
SELECT *,
DENSE_RANK() OVER (ORDER BY [user_id]) AS rn
FROM [Orders]
)
SELECT [user_id], [product_id]
FROM cte
WHERE rn <= 2 -- returns only two user after sorted by their ids
ORDER BY 1, 2
Demo
You may use DENSE_RANK function with your join query as the following:
SELECT user_id, product_id
FROM
(
SELECT U.id AS 'user_id',
P.id AS 'product_id',
DENSE_RANK() OVER (ORDER BY U.id) dr
FROM users U INNER JOIN orders O
ON O.user_id = U.id
INNER JOIN products P
ON P.id = O.product_id
) T
WHERE dr between 1 and 2
ORDER BY user_id, product_id
WHERE dr between 1 and 2 allows you to perform pagination according to the order of user ids, i.e if you want to get users from 3 to 5, it will be WHERE dr between 3 and 5.
Check this demo.

SQL join where only the max value should be returned

Looking for some help with SQL. I have the following 4 tables
Users Table
+-----------------------------+
| ID | First_Name | Last_Name |
+-----------------------------+
| 1 | Billy | O'Neal |
+----+------------+-----------+
| 2 | John | Skeet |
+----+------------+-----------+
| 3 | Ken | Stamp |
+----+------------+-----------+
| 4 | Doug | Feng |
+----+------------+-----------+
Book_CheckOut
+----+--------------+---------------+
| ID | User_ID | Book_ID |
+-----------------------------------+
| 1 | 1 | 1 |
+----+--------------+---------------+
| 2 | 2 | 3 |
+----+--------------+---------------+
| 3 | 2 | 1 |
+----+--------------+---------------+
| 4 | 2 | 2 |
+----+--------------+---------------+
| 5 | 3 | 1 |
+----+--------------+---------------+
| 6 | 1 | 4 |
+----+--------------+---------------+
| 7 | 1 | 0 |
+----+--------------+---------------+
Books
+---------+-------------+-------------+
| ID | Book_Name | Location_ID |
+-----------------------+-------------+
| 1 | Programming | 1 |
+---------+-------------+-------------+
| 2 | Cooking | 3 |
+---------+-------------+-------------+
| 3 | Dancing | 2 |
+---------+-------------+-------------+
| 4 | Sports | 1 |
+---------+-------------+-------------+
Location
+---------+-------------+
| ID | Loc_Name |
+-----------------------+
| 1 | Palo Alto |
+---------+-------------+
| 2 | San Jose |
+---------+-------------+
| 3 | Oakland |
+---------+-------------+
| 4 | Cupertino |
+---------+-------------+
What I am trying to get to is to figure out all the person with the latest book checked out. If the person doesn't have any record, he should show up. If there are no book matched such as 0 which means that the person returned all book. He should show up as well.
End results
Record
+-----------------+----------------+----------------+
| First_Name | Book_Name | Loc_Name |
+-----------------+----------------+----------------+
| Billy | | |
+-----------------+----------------+----------------+
| John | Cooking | Oakland |
+-----------------+----------------+----------------+
| Ken | Programming | Palo Alto |
+-----------------+----------------+----------------+
| Doug | | |
+-----------------+----------------+----------------+
Billy doesn't have anything since his last record in Book_CheckOut is 0 and Doug doesn't have anything since there are no record of him in Book_CheckOut.
I have tried various join with MAX() and group by but there doesn't seem to be a way to satisfy all of what I am looking for.
Any help is greatly appreciated.
try this:
select
u.first_name,
b.book_name,
l.loc_name
from user u
left join (select *
from book_checkout t0
where id = (select
max(id)
from book_checkout
where user_id = t0.user_id
)
) bc on bc.user_id = u.id
left join books b on b.id = bc.book_id
left join location l on l.id = b.location_id
subquery inside first join statement is used to select only last records for every user. But this query is considered that every user checkout only 1 book at a time.
Let me know if it works )
SELECT LC.First_Name
, ISNULL(B.Book_Name, N'') AS BookName
, ISNULL(L.Loc_Name, N'') AS Loc_Name
FROM Books AS B
INNER JOIN Book_CheckOut AS BC ON B.ID = BC.Book_ID
INNER JOIN Location AS L ON B.ID = L.ID
RIGHT OUTER JOIN (SELECT U.First_Name
, ISNULL(MAX(BC.ID), 0) AS BCID
FROM Users AS U
LEFT OUTER JOIN Book_CheckOut AS BC ON U.ID = BC.User_ID
GROUP BY U.First_Name) AS LC ON BC.ID = LC.BCID
The subquery shows Last CheckOut of all users.
select First_Name, Book_Name, Location_Name
from Users U, (select * from Books_Checkout where ID in (select max(ID) from Books_Checkout group by User_ID) and Book_ID is not null order by ID) BC, Books B, Location L
where U.ID = BC.User_ID and B.ID = BC.Book_ID and L.ID = B.Location_ID;
The above query results:
John Cooking Oakland
Ken Programming Palo Alto

Distinct on one column with multiple results

I have a problem with my query through visual basic to access database. Lets say I have two tables as bellow.
+-------+-------------+-------------+------------+
| ID | Date1 | Date2 | CustomerID |
+-------+-------------+-------------+------------+
| 1 | 1-1-2013 | 1-1-2012 | 1 |
| 2 | 1-1-2013 | 1-1-2012 | 1 |
| 3 | 1-1-2013 | 1-1-2012 | 2 |
| 4 | 1-1-2013 | 1-1-2012 | 3 |
| 5 | 1-1-2013 | 1-1-2012 | 3 |
+-------+-------------+-------------+------------+
and
+----------+---------+
| ID | Name |
+----------+---------+
| 1 | John |
| 2 | Tina |
| 3 | Patrick |
+----------+---------+
I would like to get result with only unique numbers from Customer - ID in first table like this one bellow.
+----------+----------+-------------+------------+
| ID | Date1 | Date2 | CustomerID |
+----------+----------+-------------+------------+
| 1 | 1-1-2013 | 1-1-2012 | 1 |
| 3 | 1-1-2013 | 1-1-2012 | 2 |
| 4 | 1-1-2013 | 1-1-2012 | 3 |
+----------+----------+-------------+------------+
I've tried with this query but with no luck.
sqL = " SELECT DISTINCT Order.ID, Order.Date1, Order.Date2, Customer.Name
FROM Order (Order INNER JOIN Customer ON Order.CustomerID = Customer.ID)"
But the code does not give me the result I wanted. Can you please provide me some help with my query.
UPDATED This will do it
SELECT q.OrderID, o.Date1, o.Date2, o.CustomerID, c.Name AS CustomerName
FROM
(
(SELECT CustomerID, MIN(ID) AS OrderID
FROM [Order]
GROUP BY CustomerID
) AS q INNER JOIN [Order] AS o
ON q.OrderID = o.ID
) INNER JOIN Customer AS c
ON o.CustomerID = c.ID
Output:
OrderID | Date1 | Date2 | CustomerID | CustomerName
--------|----------|----------|------------|-------------
1 | 1/1/2013 | 1/1/2012 | 1 | John
3 | 1/1/2013 | 1/1/2013 | 2 | Tina
4 | 1/1/2013 | 1/1/2012 | 3 | Patrick
UPDATE: Based on your comments your real query might look like
SELECT q.ID, o.ID_narocila, o.datum_sprejema, o.rok_izdobave, o.status_artikla, o.status_narocila o.ID_stranke, c.naziv
FROM
(
(SELECT ID_stranke, MIN(ID) AS ID
FROM Narocilo
GROUP BY ID_stranke
) AS q INNER JOIN Narocilo AS o
ON q.ID = o.ID
) INNER JOIN Stranke AS c
ON o.ID_stranke = c.ID
From the desired result set you have shown, the following query will work:
SELECT Min(T2.ID) AS MinOfID, Min(T1.Date1) AS MinOfDate1, Min(T1.Date2) AS MinOfDate2, T1.CustomerID
FROM T2 INNER JOIN T1 ON T2.ID = T1.CustomerID
GROUP BY T1.CustomerID;

Finding the customers who did not order all 3 types of pizzas

customers:
+-----------+-----------+
| cid | name |
+-----------+-----------+
| 1 | a |
| 2 | b |
| 3 | c |
+-----------+-----------+
pizza:
+-----------+-----------+
| pid | type |
+-----------+-----------+
| 1 | sausage |
| 2 | cheese |
| 3 | veggies |
| 4 | sausage |
| 5 | veggies |
| 6 | sausage |
| 7 | sausage |
+-----------+-----------+
orders:
+-----------+-----------+-----------+
| oid | cid | pid |
+-----------+-----------+-----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 6 |
| 7 | 3 | 7 |
+-----------+-----------+-----------+
I'm having some trouble wrapping my head around sql logic. How do I find the customers who did not order all 3 types of pizza? The three types are sausage, cheese and veggies. Do I need to use NOT EXIST?
You could use a having clause to look for customers that have ordered less than 3 pizza types:
select c.name
from customers c
join orders c
on c.cid = c.id
join pizza p
on p.id = c.pid
group by
c.name
having
count(distinct p.type) < 3
select cid, count(distinct type) as c from orders join pizza on orders.pid = pizza.pid group by cid having c < 3;
You can use something like this:
select c.cid,
c.name
from customers c
left join orders o
on c.cid = o.cid
left join pizza p
on o.pid = p.pid
where p."type" in ('sausage', 'cheese', 'veggies') -- if you have more pizza types list them here
group by c.cid, c.name
having count(distinct "type") <> (select count(distinct "type")
from pizza)
See SQL Fiddle with Demo
Or
select c.cid,
c.name
from customers c
left join orders o
on c.cid = o.cid
left join pizza p
on o.pid = p.pid
where p."type" in ('sausage', 'cheese', 'veggies')
group by c.cid, c.name
having count(distinct "type") <> 3 -- this is equal to the number of pizza types from the IN clause above

Generate report using SUM

I've got two SQL Server 2005 tables: MainTable and Orders.
MainTable:
| MainID | Serial |
-------------------
| 1 | A00001 |
| 2 | B00002 |
Orders:
| OrderID | MainID | Name | Value |
-----------------------------------
| 1 | 2 | John | 100 |
| 2 | 2 | Mike | 200 |
| 3 | 1 | John | 150 |
| 4 | 1 | Mike | 350 |
| 5 | 1 | John | 200 |
| 6 | 2 | John | 500 |
| 7 | 1 | Mike | 50 |
I want to get something like this:
|Serial | Name | Total |
-----------------------
| A00001 | John | 350 |
| A00002 | John | 600 |
| A00001 | Mike | 400 |
| A00002 | Mike | 200 |
SELECT
m.serial,
o.name,
SUM(o.value)
FROM
main m
INNER JOIN order o ON m.mainid = o.mainid
GROUP BY
o.name,
m.serial
select serial, name, sum(value) as total
from maintable m inner join orders o on
m.mainID = o.mainID
group by
serial, name
SELECT
M.SERIAL, O.NAME, SUM(VALUE) AS TOTAL
FROM MAINTABLE M JOIN ORDERS O ON O.MAINID=M.MAINID
GROUP BY M.SERIAL, O.NAME
select Serial , name , Total
from MainTable m,
( select MainID ,name, SUM(value) Total from Orders o group by MainID , Name ) O
where m.MainID= O.MainID