How to fix query in order to display results? - sql

I have multiple tables that i want to join in order to get the sum of a distinct column from all of them. My table structure example is as represented below.
Customers AS A
+---+-------+------------+--------+----------+
|id | name | profile_id | grp | end_date |
+---+-------+------------+--------+----------+
| 1 | cust1 | 7 | ae | (null) |
+---+-------+------------+--------+----------+
| 2 | cust2 | 2 | bz | (null) |
+---+-------+------------+--------+----------+
| 3 | cust2 | 2 | cc | not_null |
+---+-------+------------+--------+----------+
Profiles AS B
+---+-------------------+
|id | profile_name |
+---+-------------------+
| 2 | pro_cust1 |
+---+-------------------+
| 7 | pro_cust2 |
+---+-------------------+
Invoices AS C
+---+------------------+----------------+----------------+
|id | reference | scandate | customer_id |
+---+------------------+----------------+----------------+
| 1 | test_SOMETHING1 | (today) | 2 |
+---+------------------+----------------+----------------+
| 2 | test_2 | (today) | 2 |
+---+------------------+----------------+----------------+
| 3 | test_SOMETHING2 | (not_today) | 2 |
+---+------------------+----------------+----------------+
| 4 | test_SOMETHING3 | (today) | 1 |
+---+------------------+----------------+----------------+
| 5 | test_3 | (today) | 2 |
+---+------------------+----------------+----------------+
| 6 | test_SOMETHING4 | (not_today) | 1 |
+---+------------------+----------------+----------------+
Invoice_s_errors AS D
+---+------------------+----------------+----------------+
|id | reference | scandate | customer_id |
+---+------------------+----------------+----------------+
| 1 | tst_SOMETHING1 | (today) | 1 |
+---+------------------+----------------+----------------+
| 2 | tst_2 | (today) | 2 |
+---+------------------+----------------+----------------+
| 3 | tst_SOMETHING2 | (not_today) | 1 |
+---+------------------+----------------+----------------+
| 4 | tst_SOMETHING3 | (today) | 1 |
+---+------------------+----------------+----------------+
| 5 | tst_3 | (today) | 2 |
+---+------------------+----------------+----------------+
| 6 | tst_SOMETHING4 | (not_today) | 1 |
+---+------------------+----------------+----------------+
Invoice_fail AS E
+---+------------------+----------------+----------------+
|id | reference | scandate | customer_id |
+---+------------------+----------------+----------------+
| 1 | ttt_SOMETHING1 | (today) | 2 |
+---+------------------+----------------+----------------+
| 2 | ttt_2 | (today) | 1 |
+---+------------------+----------------+----------------+
| 3 | ttt_SOMETHING2 | (not_today) | 2 |
+---+------------------+----------------+----------------+
| 4 | ttt_SOMETHING3 | (today) | 2 |
+---+------------------+----------------+----------------+
| 5 | ttt_3 | (today) | 1 |
+---+------------------+----------------+----------------+
| 6 | ttt_SOMETHING4 | (not_today) | 2 |
+---+------------------+----------------+----------------+
Output:
+---------+---------------+--------+--------+
| customer+ profile | group | Total |
+---------+---------------+--------+--------+
| cust1 | pro_cust1 | ae | 2 |
+---------+---------------+--------+--------+
| cust2 | pro_cust2 | bz | 3 |
+---------+---------------+--------+--------+
Below is my code that's returning no data found . What am i doing wrong ?
SELECT
A.name as customer
,B.profile_name as profile
,A.grp as "Group"
,count(distinct C.reference) + count(distinct D.reference) + count(distinct E.reference) as "Total"
FROM
customers A
INNER JOIN
profiles B
ON
A.profile_id = B.id
INNER JOIN
invoices C
ON
A.id = C.customer_id
INNER JOIN
invoice_s_errors D
ON
A.id = D.customer_id
INNER JOIN
invoice_fail E
ON
A.id = E.customer_id
WHERE
A.end_date IS NULL
AND
(upper(C.reference) NOT LIKE ('%SOMETHING%') AND trunc(C.scandate) = trunc(sysdate))
AND
(upper(D.reference) NOT LIKE ('%SOMETHING%') AND trunc(D.scandate) = trunc(sysdate))
AND
(upper(E.reference) NOT LIKE ('%SOMETHING%') AND trunc(E.scandate) = trunc(sysdate))
GROUP BY A.name, A.grp, B.profile_name ORDER BY A.name ASC

It's your data, and because you INNER JOIN them.
The customerid's that have a "reference" without 'SOMETHING' are different in table alias E, compared to those in C & D
So change to LEFT JOIN's.
And then put some of the criteria that's now in the WHERE in the JOIN's.
Otherwise they would still act as INNER JOIN's.
SELECT
cust.name AS customer
,prof.profile_name AS profile
,cust.grp AS "Group"
,(COUNT(DISTINCT inv.reference) +
COUNT(DISTINCT inverr.reference) +
COUNT(DISTINCT invfail.reference)) AS "Total"
FROM customers cust
JOIN profiles prof ON prof.id = cust.profile_id
LEFT JOIN invoices inv
ON inv.customer_id = cust.id
AND upper(inv.reference) NOT LIKE ('%SOMETHING%')
AND trunc(inv.scandate) = trunc(sysdate)
LEFT JOIN invoice_s_errors inverr
ON inverr.customer_id = cust.id
AND upper(inverr.reference) NOT LIKE ('%SOMETHING%')
AND trunc(inverr.scandate) = trunc(sysdate)
LEFT JOIN invoice_fail invfail
ON invfail.customer_id = cust.id
AND upper(invfail.reference) NOT LIKE ('%SOMETHING%')
AND trunc(invfail.scandate) = trunc(sysdate)
WHERE cust.end_date IS NULL
GROUP BY cust.name, cust.grp, prof.profile_name
ORDER BY cust.name ASC

Related

Join two tables. Select all rows from one table and only matching values from other table?

I have the following tables:
Locations
+-------------+----------------+
| Location_ID | Location_Name |
+-------------+----------------+
| 1 | Administration |
| 2 | Parking |
| 3 | Warehouse |
| 4 | Shipping |
| 5 | Factory |
| 6 | Office |
| 7 | Processing |
+-------------+----------------+
Item_Quantity
+---------+-------------+-------------------+
| Item_ID | Location_ID | Location_Quantity |
+---------+-------------+-------------------+
| 1 | 3 | 10 |
| 1 | 5 | 50 |
| 2 | 3 | 7 |
+---------+-------------+-------------------+
I am trying to get a list of all Location_IDs and Location_Names with the Location_Quantity for a specified Item_ID.
The expected result for Item_ID = 1 would be this:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 1 | Administration | 0 |
| 2 | Parking | 0 |
| 3 | Warehouse | 10 |
| 4 | Shipping | 0 |
| 5 | Factory | 50 |
| 6 | Office | 0 |
| 7 | Processing | 0 |
+-------------+----------------+-------------------+
The expected result for Item_ID = 2 would be this:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 1 | Administration | 0 |
| 2 | Parking | 0 |
| 3 | Warehouse | 7 |
| 4 | Shipping | 0 |
| 5 | Factory | 0 |
| 6 | Office | 0 |
| 7 | Processing | 0 |
+-------------+----------------+-------------------+
I have tried the following queries:
SELECT l.Location_ID, l.Location_Name, iq.Location_Quantity
FROM Locations l
LEFT JOIN Item_Quantity iq ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
SELECT l.Location_ID, l.Location_Name, iq.Location_Quantity
FROM Item_Quantity iq
LEFT JOIN Locations l ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
SELECT l.Location_ID, l.Location_Name, Location_Quantity = iif(iq.Location_Quantity IS NOT NULL, iq.Location_Quantity, 0)
FROM Locations l
LEFT JOIN Item_Quantity iq ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
All queries return only the rows with entries in Item_Quantity.
This is what I am getting for Item_ID = 1 for any of the above queries:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 3 | Warehouse | 10 |
| 5 | Factory | 50 |
+-------------+----------------+-------------------+
I would have thought a Left Join on the Locations table would give me all of the rows from the specified columns, but I must be understanding something incorrectly?
Can anyone see what I am doing wrong here?
The condition needs to go in the ON clause. Otherwise, the WHERE clause turns the outer join into an inner join.
You also want to convert the NULL to a 0, so use COALESCE():
SELECT l.Location_ID, l.Location_Name, COALESCE(iq.Location_Quantity, 0) as Location_Quantity
FROM Locations l LEFT JOIN
Item_Quantity iq
ON l.Location_ID = iq.Location_ID AND iq.Item_ID = #Item_ID;

Simplifying a query with a LIMIT in a subquery and WHERE clauses duplicated in the subquery and outer query

SQL Fiddle
Assuming the following tables:
customers
+----+------------------------+------------+------------+
| id | company | first_name | last_name |
+----+------------------------+------------+------------+
| 1 | MDD | T | H |
| 2 | Aliance Magnet A LLP | A | Wilkinson |
| 3 | MAF | C | G |
| 4 | QL | F | B |
| 5 | ARL | S | P |
| 6 | Q Corp. | H | H |
| 7 | VQDA | L | W |
| 8 | AESC | E | W |
| 9 | Placement Incorporated | C | Mendez |
| 10 | Numpties United | Y | Cunningham |
+----+------------------------+------------+------------+
transactions
+----+-----------+-------------+------+
| id | form_type | customer_id | due |
+----+-----------+-------------+------+
| 1 | invoice | 9 | 1 |
| 2 | payment | 1 | 6 |
| 3 | invoice | 7 | 9 |
| 4 | payment | 9 | 4 |
| 5 | invoice | 7 | 5 |
| 6 | payment | 3 | 5 |
| 7 | invoice | 9 | 5 |
| 8 | invoice | 9 | 10 |
| 9 | invoice | 10 | 1 |
| 10 | invoice | 2 | 4 |
+----+-----------+-------------+------+
The following query finds the customers with invoice transactions > 0, but only returns the records related to the first three customers.
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM (
SELECT DISTINCT c.*
FROM customers AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name
LIMIT 3
) AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name;
results
+----------+-------------+------------------------+------------+------------+------+
| trans_id | customer_id | company | first_name | last_name | due |
+----------+-------------+------------------------+------------+------------+------+
| 10 | 2 | Aliance Magnet A LLP | A | Wilkinson | 4 |
| 9 | 10 | Numpties United | Y | Cunningham | 1 |
| 1 | 9 | Placement Incorporated | C | Mendez | 1 |
| 7 | 9 | Placement Incorporated | C | Mendez | 5 |
| 8 | 9 | Placement Incorporated | C | Mendez | 10 |
+----------+-------------+------------------------+------------+------------+------+
Is there a way, e.g. using window functions or common table expressions that will avoid duplicating WHERE t.due > 0 AND t.form_type = 'invoice' and the ORDER BY clause in the inner and outer queries? Or some other way to get the same results with a single SQL query?
You could use DENSE_RANK:
WITH cte AS (
SELECT t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due,
DENSE_RANK() OVER(ORDER BY c.company, c.first_name, c.last_name) rn
FROM customers AS c
JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0 AND t.form_type = 'invoice'
)
SELECT * FROM cte WHERE rn <= 3;
DBFiddle Demo

MS Access SQL getting results from different tables and sorting by date

i hope my description will be enough. i tried to remove all non-significant fields.
i have 5 tables (Customer, Invoice, Items, Invoice_Item, Payment):
Customer fields and sample date are:
+----+------+
| ID | Name |
+----+------+
| 1 | John |
| 2 | Mary |
+----+------+
Invoice fields and sample date are:
+----+-----------+----------+------+
| ID | Date | Customer | Tax |
+----+-----------+----------+------+
| 1 | 1.1.2017 | 1 | 0.10 |
| 2 | 2.1.2017 | 2 | 0.10 |
| 3 | 3.1.2017 | 1 | 0.10 |
| 4 | 3.1.2017 | 2 | 0.10 |
| 5 | 8.1.2017 | 1 | 0.10 |
| 6 | 11.1.2017 | 1 | 0.10 |
| 7 | 12.1.2017 | 2 | 0.10 |
| 8 | 13.1.2017 | 1 | 0.10 |
+----+-----------+----------+------+
Item fields and sample data are:
+----+--------+
| ID | Name |
+----+--------+
| 1 | Door |
| 2 | Window |
| 3 | Table |
| 4 | Chair |
+----+--------+
Invoice_Item fields and sample data are:
+------------+---------+--------+------------+
| Invoice_ID | Item_ID | Amount | Unit_Price |
+------------+---------+--------+------------+
| 1 | 1 | 4 | 10 |
| 1 | 2 | 2 | 20 |
| 1 | 3 | 1 | 30 |
| 1 | 4 | 2 | 40 |
| 2 | 1 | 1 | 10 |
| 2 | 3 | 1 | 15 |
| 2 | 4 | 2 | 12 |
| 3 | 3 | 4 | 15 |
| 4 | 1 | 1 | 10 |
| 4 | 2 | 20 | 30 |
| 4 | 3 | 15 | 30 |
| 5 | 1 | 4 | 10 |
| 5 | 2 | 2 | 20 |
| 5 | 3 | 1 | 30 |
| 5 | 4 | 2 | 40 |
| 6 | 1 | 1 | 10 |
| 6 | 3 | 1 | 15 |
| 6 | 4 | 2 | 12 |
| 7 | 3 | 4 | 15 |
| 8 | 1 | 1 | 10 |
| 8 | 2 | 20 | 30 |
| 8 | 3 | 15 | 30 |
+------------+---------+--------+------------+
The reason the price is in this table not in the item table is because it is customer specific price.
Payment fields are:
+----------+--------+-----------+
| Customer | Amount | Date |
+----------+--------+-----------+
| 1 | 40 | 3.1.2017 |
| 2 | 10 | 7.1.2017 |
| 1 | 60 | 10.1.2017 |
+----------+--------+-----------+
so my report should be combine all tables and sort by DATE (either from Invoice or Payment) for a certain customer.
so for e.g. for customer John (1) it should be like:
+------------+----------------+---------+-----------+
| Invoice_ID | Invoice_Amount | Payment | Date |
+------------+----------------+---------+-----------+
| 1 | 171 | - | 1.1.2017 |
| 3 | 54 | - | 3.1.2017 |
| - | - | 40 | 3.1.2017 |
| 5 | 171 | - | 8.1.2017 |
| - | 10 | 60 | 10.1.2017 |
| 6 | 44.1 | - | 11.1.2017 |
| 8 | 954 | - | 13.1.2017 |
+------------+----------------+---------+-----------+
it is sorted by date, Invoice amount is (sum of (Amount* unit price)) * (1-tax)
i started with union but then got lost.
here is my try:
SELECT Inv_ID as Num, SUM(Invoice_Items.II_Price*Invoice_Items.II_Amount) AS Amount, Inv_Date as Created
FROM Invoice INNER JOIN Invoice_Items ON Invoice.Inv_ID = Invoice_Items.II_Inv_ID
UNION ALL
SELECT Null as Num, P_Value as Amount, P_Date as Created
FROM Payments
ORDER BY created ASC
Your help is appreciated!
Thanks
You can generate the report you requested using the following SQL script:
SELECT CustomerID,Invoice_ID,Invoice_Amount,Payment,Date
FROM (
SELECT c.ID AS CustomerID, i.ID AS Invoice_ID, SUM((t.Amount * t.UnitPrice)*(1-i.tax)) AS Invoice_Amount, NULL AS Payment,i.Date
FROM (Customer c
LEFT JOIN Invoice i
ON c.ID = i.Customer)
LEFT JOIN Invoice_Item t
ON i.ID = t.Invoice_ID
GROUP BY c.ID, i.ID,i.Date
UNION
SELECT c.ID AS CustomerID,NULL AS Invoice_ID, NULL AS Invoice_Amount, p.Amount AS Payment, p.Date
FROM Customer c
INNER JOIN Payment p
ON c.ID = p.Customer ) a
ORDER BY CustomerID, Date, Payment ASC
Note: I've added CustomerID to the output so you know what customer the data corresponds to.
here is the Answer which worked for me, a bit corrected from #Catzeye Answer , which didnt show the second part of the Union.
SELECT c.ID AS CustomerID,NULL AS Invoice_ID, NULL AS Invoice_Amount, p.Amount AS Payment, p.Date
FROM Customer c
INNER JOIN Payment p
ON c.ID = p.Customer
UNION ALL
SELECT c.ID AS CustomerID, i.ID AS Invoice_ID, SUM((t.Amount * t.Unit_Price)*(1-i.tax)) AS Invoice_Amount, NULL AS Payment,i.Date
FROM (Customer c
INNER JOIN Invoice i
ON c.ID = i.Customer)
INNER JOIN Invoice_Item t
ON i.ID = t.Invoice_ID
GROUP BY c.ID, i.ID,i.Date
ORDER BY CustomerID, Date, Payment;

Join a table representing a "transfer" between two rows of another table

Sorry for the confusing title; however a description and illustration should hopefully clear it up.
Essentially, I have the table A representing instances of a transfer of an 'amount' between rows of table B. I wish to join A with B so that I can display the details of the transfer:
================= A ===================
+-----+-----------+----------+--------+
| AID | fromID(FK) | toID(FK) | amount |
+-----+-----------+----------+--------+
| 1 | 1 | 5 | 100 |
| 2 | 1 | 3 | 150 |
| 3 | 5 | 3 | 500 |
| 4 | 1 | 5 | 200 |
| 5 | 4 | 5 | 800 |
| 6 | 3 | 5 | 15 |
+----+------------+----------+--------+
and
==== B =====
+----+------+
| BID | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
I wish to join them and produce a "from name" column and a "to name" like:
+-----+------+----+--------+
| AID | from | to | amount |
+-----+------+----+--------+
| 1 | a | e | 100 |
| 2 | a | c | 150 |
| 3 | e | c | 500 |
| 4 | a | e | 200 |
| 5 | d | e | 800 |
| 6 | c | e | 15 |
+-----+------+----+--------+
You can join a on b twice:
SELECT aid, from_b.name, to_b.name, amount
FROM a
JOIN b from_b ON from_b.bid = a.fromid
JOIN b to_b ON to_b.bid = a.toid
Do a JOIN between the tables like below but you will have to join table B twice
select a.AID,
b.name as [from],
b1.name as [to],
a.amount
from A a
join B b on a.fromID(FK) = b.BID
join B b1 on a.toID(FK) = B.bid;
You can do this with out join.
Fiddle with sample data
select aid,
(select name from b where a.fromid = bid) as "from",
(select name from b where a.toid = bid) as "to",
amount
from a

Finding number of types of accounts from each customer

I am having a lot of trouble with trying to construct a query that will give me the name of each customer and the number of different types of accounts each has. The three types are Checkings, Savings, and CD.
customers:
+--------+--------+
| cid | name |
+--------+--------+
| 1 | a |
| 2 | b |
| 3 | c |
+--------+--------+
accounts:
+-----------+-----------+
| aid | type |
+-----------+-----------+
| 1 | Checkings |
| 2 | Savings |
| 3 | Checkings |
| 4 | CD |
| 5 | CD |
| 6 | Checkings |
+-----------+-----------+
transactions:
+--------+--------+--------+
| tid | cid | aid |
+--------+--------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 4 |
| 7 | 1 | 6 |
+--------+--------+--------+
The expected answer would be:
a, 3
b, 1
c, 1
Getting the names is simple enough, but how can I keep count of each individual's account as well as compare the accounts to make sure that it is not the same type?
just add DISTINCT inside the COUNT
SELECT a.cid, a.name, COUNT(DISTINCT c.type) totalCount
FROM customers a
INNER JOIN transactions b
ON a.cis = b.cid
INNER JOIN accounts c
ON b,aid = c.aid
GROUP BY a.cid, a.name
Query:
SQLFiddleExample
SELECT
a."name",
COUNT(DISTINCT c."type") totalCount
FROM customers a
INNER JOIN transactions b
ON a."cid" = b."cid"
INNER JOIN accounts c
ON b."aid" = c."aid"
GROUP BY a."cid", a."name"
ORDER BY totalCount DESC
Result:
| NAME | TOTALCOUNT |
---------------------
| a | 3 |
| b | 1 |
| c | 1 |