Left outer join in rails - mysql

I have two objects - Customers & Customer Checkins. Each customer has many checkins and each checkin belongs to one customer.
I need to come up with a query that outputs the complete list of customers together with their checkin count per month. My current query is shown below:
customer = Customer.where(account_id: 139).joins('LEFT JOIN
customer_checkins on customer_checkins.customer_id =
customers.id').uniq.select("COUNT(*) as count, customers.created_at as
created_at, customers.name, customers.telephone, customers.mobile,
customers.email, customers.gender, customers.city,
customers.birthday,date_format(customer_checkins.created_at, '%b') as
'month', customers.id").group("customer_checkins.customer_id,
date_format(customer_checkins.created_at, '%b %Y')")
This query does not list all the customers. What is wrong with my code?

It does not list all the customers because you're only searching for ones with account_id: 139.
Replace
customer = Customer.where(account_id: 139)...
With
customer = Customer...

Related

SQL for different date data manipulation

Suppose I have two tables (Customers and Invoices). I want to know newest customers based on their first invoice date for x month. For example, a customer might have joined/registered in November but it is possible that he has placed his FIRST ORDER in January. So he is a new customer for my criteria.
Sample tables are given below:
DESIRED RESULTS:
Below is just one example for above sample data. C5 customer joined in August 2019 but he placed his first order in January 2020. So for me, he is a new customer.
If you want to filter on customers that have placed their first order after a given date, just use aggregation:
select customer, min(date) first_invoice_date
from invoices
group by customer
having first_invoice_date > #somedate
You can adapt (or remove) the having clause as needed.
If you want the details of the customer (eg its name and coutry), then you can join the customers table:
select c.*, o.first_invoice_date
from customers c
inner join (
select customer, min(date) first_invoice_date
from invoices
group by customer
having first_invoice_date > #somedate
) i on i.customer = c.id

Counting amount of base records in (My)Sql 1-to-many relation

I have these two tables:
invoices (contains id field)
contracts (contains fk to invoice + 'code' field)
Let's say I have one record in the invoices table and two records in the contracts table. Both records in the contracts table point to the same invoice record.
Desire: I'd like to count the amount of invoices.
What I've got so far:
select
c.code, count(*)
from
invoices i
join
contracts c
on
c.invoice_id = i.id
group by
c.code
Although the count shows two instead of the desired 1. I understand that this is because of the join on the contract table, but not sure how to fix this.
Try with COUNT(DISTINCT i.id); it should count the different invoice id's in the resultset.
select
c.code, count(distinct i.id)
from
invoices i
join
contracts c
on
c.invoice_id = i.id
group by
c.code
You want the number of invoices?
select count(*)
from invoices i
You want invoices with contracts?
select count( distinct c.invoice_id)
from
contracts
Your code finds number of contracts per each invoice

Working with multiple tables

I have these 4 tables with different rows and columns and I need to get certain values. I am having some difficulty with a few and cannot seem to get it.
customers table:
customerID: integer
fName: string
lName: string
items table:
itemID: integer
description: string
price: float
orders table:
orderID: integer
itemID: integer
aID: integer
customerID: integer
date: date
addresses table:
aID: integer
housenum: integer
streetName: string
town:string
state: string
zip:integer
The values I am looking to get are:
List the itemID and description of all items for which there is only one order.
Now I can get the itemID and description, however I am not sure how to nail it down to items only ordered once. I am thinking of using JOIN and GROUP BY but I cannot seem to find the correct combination.
SELECT itemID,
description
FROM items
GROUP BY itemID
List all customers who have shipped items to multiple addresses.
This one I have to use JOIN somehow, since the customerID would be the same but not the address?
SELECT customerID,
streetName,
housenum
FROM addresses
JOIN customers
ON addresses.streetName = customers.customerID
Return the total revenue generated by all of the orders.
Couldn't I just use:
SELECT SUM(price),
orderID
FROM items
JOIN orders
ON orders.orderID = items.price ?
List the first and last names of all customers who have had the same item shipped to at least two different addresses.
SELECT fName,
lName,
itemID,
streetName,
housenum
FROM addresses,
customers,
items,
somehow join multiple(2+)?
I may not be right with all the queries but I have found the items listed on the question very interesting and decided to give it a try.
items sold only once
SELECT o.itemID,
i.description
FROM orders o
LEFT JOIN items i
ON i.id = o.itemID
GROUP BY o.itemID
HAVING COUNT(*) = 1;
On this query, you need to join the the items table in order to retrieve the description and GROUP BY it by the itemID so you can specifically use COUNT for each itemID which we can then use with HAVING to obtain on the ones with 1 order.
Customer that have shipped to diff address
SELECT COUNT(DISTINCT addressID) as diff_address,
c.fName,
c.lName
FROM orders o
LEFT JOIN customers c
ON o.customerID = c.id
GROUP BY o.customerID
HAVING diff_address > 1;
Here I am using the DISTINCT function to eliminate duplicated address and GROUP BY the customerID so I can have a list of unique address per customer and again using HAVING we get the customers that have shipped products with more than 1 different address. Am also using JOIN on the customer table to obtain their needed information.
Total revenue
SELECT SUM(i.price) as total
FROM orders o
LEFT JOIN items i
ON o.itemID = i.id;
I guess this one is the simplest one here, by using JOIN on the items table, I am able to read each item price which I can then sum per order to have a gran total of sales.
List of customers that have shipped the same item to at least 2 different address
SELECT c.fName,
c.lName,
COUNT(oi.itemID) AS `total items with diff address >= 2`
FROM customers c
JOIN (SELECT customerID,
itemID,
COUNT(DISTINCT addressID) AS diff_address
FROM orders
GROUP BY itemID, customerID
HAVING diff_address >= 2
) AS oi
ON oi.customerID = c.id
GROUP BY c.id;
This one was the most complicated one, hope I can explain it. First I started it with the sub-query where I can list all items per customer that have multiple address, then by using JOIN on the customerID I can count how many different items that given customer have shipped with 2 or more diff address.
Here is a LIVE DEMO.
If you see any of the queries are not good or have any advice kindly do tell me so I can improve it.

MySQL query with JOINS and GROUP BY

I'm building a MySQL query but I can't seem to get it right.
I have four tables:
- customers
- orders
- sales_rates
- purchase_rates
There is a 1:n relation 'customernr' between customers and orders.
There is a 1:n relation 'ordernr' between orders and sales_rates.
There is a 1:n relation 'ordernr' between orders and purchase_rates.
What I would like to do is produce an output of all customers with their total purchase and sales amounts.
So far I have the following query.
SELECT c.customernr, c.customer_name, SUM(sr.sales_price) AS sales_price, SUM(pr.purchase_price) AS purchase_price
FROM orders o, customers c, sales_rates sr, purchase_rates pr
WHERE o.customernr = c.customernr
AND o.ordernr = sr.ordernr
AND o.ordernr = pr.ordernr
GROUP BY c.customer_name
The result of the sales_price and purchase_price is far too high. I seem to be getting double counts. What am I doing wrong? Is it possible to perform this in a single query?
Thank for your response!
It seems that the problem is that when you join the orders table to the tables with sales rates and purchase rates, you are getting the cartesian product of these two latter tables. I.e each row in these two tables are repeated once for each correponding row in the other table. The following query should solve this problem by summing the rates for each order before joining the sales rates and purchase rates to the other tables:
SELECT c.customernr, c.customer_name,
SUM(sr.sales_price) AS sales_price,
SUM(pr.purchase_price) AS purchase_price
FROM customers c
INNER JOIN orders o
ON o.customernr = c.customernr
LEFT JOIN (SELECT ordernr, SUM sales_price) AS sales_price
FROM sales_rates
GROUP BY ordernr) sr
ON sr.ordernr = o.ordernr
LEFT JOIN (SELECT ordernr, SUM(purchase_price) AS purchase_price
FROM purchase_rates
GROUP BY ordernr) pr
ON pr.ordernr = o.ordernr
GROUP BY c.customernr, c.customer_name;
It doesn't look like you are grouping by the customer. C.customerid or something like that.

Query to find how many purchases were made this year, For each Artist

I need to list the Artist's Id_no, last name (lname) and how any purchases have been made on their releases this year. The only information about the purchases is different purchase dates.
My code so far :
SELECT id_no, lname, purchasedate AS num_ops
FROM Artist JOIN Sales ON Artist.id_no = Sales.artist
WHERE DATE_SUB(CURDATE(),INTERVAL 1 YEAR) <= purchasedate
However this only returns the Id_no's who have made sales, and I need all even if it is 0. On distrinctl name, and num_ops is a date of their first purchasedate. I need to alter this code to list how many purchases they have been involved in within this year. I've tried using COUNT(purchasedate) however this just returns one row. I wish the returning table to return:
1st Column: The Artist's ID Number
2nd Column: The Aritist's Last Name
3rd Column: The number of people who have purchased their CD's (which is a count of purchasedate)
I am struggling with the 3rd column majorly, and any help would be greatly appriciated.
You need:
a left join
to move the date condition into the ON clause of the JOIN
use the count(*) aggregating function
Like this:
SELECT id_no, lname, count(purchasedate) as num_ops
FROM Artist
LEFT JOIN Sales ON Artist.id_no = Sales.artist
AND DATE_SUB(CURDATE(),INTERVAL 1 YEAR) <= purchasedate
GROUP BY id_no, lname
The LEFT JOIN will ensure a row is returned for every artist even if there are no sales
By moving the date condition into the join that will still return a row for every artist even if there's no sale for the year. If the condition is left in the WHERE clause, that would filter out artists that didn't make sales in the last year.
A key point here is that the join condition may contain conditions unrelated to the keys involved - that's how you get conditional joins, which is what you want here to make the left join still function correctly