SQL Server : max date from two tables - sql-server-2008

I need to find the last time a customer ordered an item for all customers and all items. I figured I would start with the last time the customer ordered anything. This would involve two tables oeordhdr_sql and oehdrhst_sql both have cus_no and ord_dt.
I am getting an error on the on
select
arcusfil.cus_no, Order_Date.orddt
from
arcusfil_sql arcusfil
join
(SELECT
cus_no, MAX(MaxDate) AS orddt
FROM
(SELECT
cus_no, MAX(ord_dt) AS MaxDate
FROM
oeordhdr_sql
GROUP BY
cus_no
UNION
SELECT
cus_no, MAX(ord_dt) AS MaxDate
FROM
oehdrhst_sql
GROUP BY
cus_no) Order_date ON arcusfil.cus_no = Order_date.cus_no

You're missing a closing ) before the ON to close the inner table that you're using for the JOIN
select
arcusfil.cus_no, Order_Date.orddt
from
arcusfil_sql arcusfil
join
(SELECT
cus_no, MAX(MaxDate) AS orddt
FROM
(SELECT
cus_no, MAX(ord_dt) AS MaxDate
FROM
oeordhdr_sql
GROUP BY
cus_no
UNION
SELECT
cus_no, MAX(ord_dt) AS MaxDate
FROM
oehdrhst_sql
GROUP BY
cus_no) MaxDates
) Order_date ON arcusfil.cus_no = Order_date.cus_no
I like to use CTE's for queries such as this to keep things cleaner:
;WITH MaxDates AS (
SELECT cus_no, MAX(ord_dt) AS MaxDate
FROM oeordhdr_sql
GROUP BY cus_no
UNION
SELECT cus_no, MAX(ord_dt) AS MaxDate
FROM oehdrhst_sql
GROUP BY cus_no
),
Order_Date AS (
SELECT cus_no, MAX(MaxDate) AS orddt
FROM MaxDates
GROUP BY cus_no
)
SELECT arcusfil.cus_no, Order_Date.orddt
FROM arcusfil_sql arcusfil
INNER JOIN Order_Date on arcusfil.cus_no = Order_date.cus_no

Related

Mysql Group By get latest record with Count

This is my customer table.
I want to group by emp_id alongwith the count. But Group By gets the 'first' record and not the 'newest' one.
I have tried various queries, like this
SELECT id, emp_id, COUNT( * ) AS count, created_at
FROM customer c
WHERE created_at = (
SELECT MAX( created_at )
FROM customer c2
WHERE c2.emp_id = c.emp_id
)
GROUP BY emp_id
ORDER BY created_at DESC
LIMIT 0 , 30
But cannot get the count. Please help.
Edit: this answer doesn't help to obtain count
Try joining to a subquery:
SELECT c1.id, c1.emp_id, c1.created_at, c2.cnt
FROM customer c1
INNER JOIN
(
SELECT emp_id, MAX(created_at) AS max_created_at, COUNT(*) AS cnt
FROM customer
GROUP BY emp_id
) c2
ON c1.emp_id = c2.emp_id AND c1.created_at = c2.max_created_at;
please try this
SELECT cust1.id, cust1.emp_id, cust1.created_at, cust2.cnt
FROM customer cust1
INNER JOIN
(
SELECT emp_id, MAX(created_at) AS max_created_at, COUNT(*) AS count
FROM customer
GROUP BY emp_id
) cust2
ON cust1.emp_id = cust2.emp_id AND cust1.created_at = cust2.max_created_at;

MySQL Query - SUM of COUNT from multiple tables

I have three tables:
customers: id, name
contracts_jewels: id, customer_id, paid, transferred, final_date
contracts_objects: id, customer_id, paid, transferred, final_date
As you see, the structure of the last two tables is the same.
The "paid" and the "transferred" fields contain the value 0 or 1.
What I need is to make a query which should return all the clients (no matter if they have contracts or not), and for each client:
id, name, count_contracts_all, count_contracts_active
where:
count_contracts_all would mean the sum of [SELECT COUNT( * ) FROM
contracts_jewels WHERE customer_id=3 (for example)] and [SELECT
COUNT( * ) FROM contracts_objects WHERE customer_id=3 (for example)]
count_contracts_active would mean the sum of [SELECT COUNT( * ) FROM
contracts_jewels WHERE customer_id=3 AND final_date>=Now() AND paid=0
AND transferred=0] and [SELECT COUNT( * ) FROM contracts_objects WHERE
customer_id=3 AND final_date>=Now() AND paid=0 AND transferred=0]
Any idea? Would you please help me? Thank you!
You can count the contracts separately and then just join them up to the customers:
SELECT
c.id,
COALESCE(oc.active_count,0) + COALESCE(jc.active_count,0) as count_contracts_active,
COALESCE(oc.total_count,0) + COALESCE(jc.total_count,0) as count_contracts_all
FROM customers c
LEFT JOIN (
SELECT
customer_id
COUNT(*) as total_count,
COUNT(IF(final_date>=Now() AND paid=0 AND transferred=0,1,NULL)) as active_count
FROM contracts_jewels
GROUP BY customer_id
) as oc ON oc.customer_id = c.id
LEFT JOIN (
SELECT
customer_id
COUNT(*) as total_count,
COUNT(IF(final_date>=Now() AND paid=0 AND transferred=0,1,NULL)) as active_count
FROM contracts_objects
GROUP BY customer_id
) as jc ON jc.customer_id = c.id
One fast solution I can think of right now is:
SELECT COUNT(`temp_table`.*) FROM (
SELECT * FROM contracts_jewels WHERE customer_id=3 UNION ALL
SELECT * FROM contracts_objects WHERE customer_id=3) AS `temp_table`
AND
SELECT COUNT(`temp_table`.*) FROM (
SELECT * FROM contracts_jewels WHERE customer_id=3 AND final_date>=Now() AND paid=0 AND transferred=0 UNION ALL
SELECT * FROM contracts_objects WHERE customer_id=3 AND final_date>=Now() AND paid=0 AND transferred=0) AS `temp_table`
You can join each of those tables twice and add their corresponding COUNTs in your result:
SELECT
c.id,
(COUNT(cj1.id)+COUNT(co1.id)) AS count_contracts_all,
(COUNT(cj2.id)+COUNT(co2.id)) AS count_contracts_active
FROM
customers c
LEFT OUTER JOIN contracts_jewels cj1 ON c.id = cj1.customer_id
LEFT OUTER JOIN contracts_objects co1 ON c.id = co1.customer_id
LEFT OUTER JOIN contracts_jewels cj2 ON
c.id = cj2.id AND
cj2.final_date >= NOW() AND
cj2.paid = 0 AND
cj2.transferred = 0
LEFT OUTER JOIN contracts_object co2 ON
c.id = co2.id AND
co2.final_date >= NOW() AND
co2.paid = 0 AND
co2.transferred = 0
GROUP BY c.id
Note: I haven't run this, but hopefully it sets you in the right direction.
simple solution:
SELECT SUM(c) FROM (
SELECT COUNT(1) as c FROM `tbl1` where ...
UNION
SELECT COUNT(1) as c FROM tbl2 where ...
UNION
SELECT COUNT(1) as c FROM tbl3 where ...
) al

a better way to get customers who placed maximum and minimum number of orders

My question is to find all those customers who have made the minimum and maximum no of orders.
what i could come up with is this :
select customer_id , count(order_id) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
having count(order_id) >=
all(select count(order_id)
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id)
or count(order_id) <=
all(select count(order_id)
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id);
it gives me the correct output , but i have to do the same join 3 times. is there some better way to do this ?
Is this better? I don't know...
SELECT x.*
FROM
( SELECT customer_id
, COUNT(*) cnt
FROM orders
GROUP
BY customer_id
) x
JOIN
( SELECT MIN(cnt) min_cnt
, MAX(cnt) max_cnt
FROM
( SELECT customer_id
, COUNT(*) cnt
FROM orders
GROUP
BY customer_id
) n
) y
ON y.min_cnt = x.cnt
OR y.max_cnt = x.cnt;
I realised, that you cannot run it in a single query without calling same subqueries several times. As you refer min(numOrders) and max(numOrders) you have to store numOrders as a temporal table or to calculate it twice. Most likely the DBMS will cache your requests if they are identical, so you should not worry about wasting resources.
So your query must be a bit rewrited and is good enough. My variant is
select customer_id , count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id
having
num = (SELECT min(num) FROM (select count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id) numOrders)
or
num = (SELECT max(num) FROM (select count(order_id) as num
from customers right outer join orders using(customer_id)
group by customer_id) numOrders)
You can check it working with this sqlfiddle
If I absolutely felt this must be a single query, I'd probably just use a union all.
(
select
customer_id,
count(*) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
order by num desc limit 1
)
union all
(
select
customer_id,
count(*) as num
from bab_customer right outer join bab_order_details using(customer_id)
group by customer_id
order by num asc limit 1
)
Originally, I'd been thinking this would perform better due to avoiding joins on intermediate values. However, I'm not seeing any performance gain here. (If it was faster, then limit could be raised to some arbitrary number and the application could trim out a few extra values without losing much of the increased performance)
But, no dice.

MAX aggregate function in sql server

I wrote the following query to return the the records with the latest date.
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
order by fs.company_id
The query all works fine. But I need to retrieve the record with all related columns attached to it. Such as, id, title, desc and etc.
How can I retrieve the records with its corresponding columns?
Couple ways of doing so :
-- 1.
SELECT a.*
FROM field_sale a
INNER JOIN
(
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
)b
ON b.company_id = a.company_id AND b.latestcreatedate = a.create_dt
order by a.company_id;
-- 2.
SELECT b.* FROM
(
SELECT a.* , ROW_NUMBER()
OVER (PARTITION BY a.company_id ORDER BY a.create_dt DESC)
AS rn
FROM field_sale a
)b WHERE b.rn = 1
ORDER BY company_id
WITH t AS (
SELECT fs.company_id,
fs.create_dt AS latestcreatedate,
id,
title,
etc,
ROW_NUMBER() OVER ( PARTITION BY fs.company_id ORDER BY fs.create_dt DESC ) AS rowNum
FROM field_sale fs
)
SELECT t.company_id,
t.latestcreatedate,
t.id,
t.title,
t.etc
FROM t
WHERE t.rowNum = 1
ORDER BY t.company_id

MySQL Query optimization required

I've got a slow performing query. I know using a dependent subquery is bad, but I can't think of another way to get the data I want.
Essentially, I want to flag customers who have at least 50 invoices in the past 6 months, but no invoices this month.
This is what I have currently:
select
Customer.name,
Customer.id,
Customer.latitude,
Customer.longitude
from
Customer
where
EXISTS (
SELECT
*
FROM
Invoice_Header
WHERE
Invoice_Header.inv_date BETWEEN '2011-03-02' AND '2011-10-02'
AND
Invoice_Header.account_number = Customer.account_number
HAVING COUNT(invoice_num) > 50
)
AND NOT EXISTS (
SELECT *
FROM
Invoice_Header
WHERE
InvHead.inv_date > '2011-10-02'
AND
InvHead.account_number = Customer.account_number
)
Group by name;
Customer table has about 12k record, Invoice_Header has about 2mill records.
I have indexes on inv_date, account_number (in both tables).
Any suggestions for how to speed this up would be appreciated.
This should eliminate the correlated subqueries and be significantly faster:
SELECT c.name, c.id, c.latitude, c.longitude
FROM Customer c
INNER JOIN (
SELECT account_number
FROM Invoice_Header ih
WHERE ih.inv_date BETWEEN '2011-03-02' AND '2011-10-02'
GROUP BY account_number
HAVING COUNT(*) > 50
MINUS
SELECT DISTINCT account_number
FROM Invoice_Header ih
WHERE ih.inv_date > '2011-10-02'
) tbl
ON tbl.account_number = c.account_number
I would suggest:
SELECT
c.name,
c.id,
c.latitude,
c.longitude
FROM
Customer AS c
INNER JOIN (
SELECT account_number, count(*) AS invoice_count
FROM Invoice_Header
WHERE inv_date >= '2011-03-02' AND inv_date <= '2011-10-02'
GROUP BY account_number
) AS lsm
ON c.account_number = lsm.account_number
LEFT JOIN (
SELECT account_number, count(*) AS invoice_count
FROM Invoice_Header
WHERE inv_date > '2011-10-02'
GROUP BY account_number
) AS lm
ON c.account_number = lm.account_number
WHERE
lsm.invoice_count >= 50
AND IFNULL(lm.invoice_count, 0) = 0
select
C.name,
C.id,
C.latitude,
C.longitude,
I.account_number,
count( IF(I.inv_date>='2011-03-02' AND I.inv_date <='2011-10-02',I.inv_date,NULL )) as inv_count_6,
count( IF(I.inv_date > '2011-10-02',I.inv_date,NULL )) as inv_count_1
from Customer C
LEFT JOIN Invoice_Header I
ON C.account_number = I.account_number
GROUP BY C.id, I.account_number
HAVING inv_count_6 >= 50 AND inv_count_1=0
WHERE I.inv_date BETWEEN '2011-03-02' AND '2011-10-02'
Notes:
1.The invoices is AT LEAST 50. so the condition is >=50 not >50.
2.You have to add index to the column inv_date
Try run your query with explain and see if other indexs are needed .