SQL: Average number of applications per customer for last x months - sql

I have 3 tables Customer, Applications, ApplicationHistory. I have to retrieve following data:
Get the average number of applications per customer for last 3 months
Get the number of customers with atleast one or more applications for last 3 months
I had been trying group by however having following issues:
ApplicationHistory table has more than one entries for each application, now sure how to eliminate them &
Note: Have included Customer Table as need to filter data by customertype
Can you please suggest how can I get this right?
Many thanks,
My Solution ( does not work )
SELECT a.ApplicationId, a.CustomerId, count(*) count
from [application] a
inner join [applicationhistory] ah on a.ApplicationId = ah.ApplicationId
inner join Customer c on c.CustomerId = a.CustomerId
where ah.EventDate between #StartDateFilter and #EndDateFilter
--c.CustomerType in ( A, B)
group by a.ApplicationId, a.CustomerId
Table Structure:
Customer
Name CustomerId CustomerType
test1 1 A
test2 2 B
Applications
ApplicationId CustomerId
3 1
4 1
5 2
6 2
7 2
ApplicationHistory
ApplicationId EventDate EventType
3 2014-12-01 New
3 2014-12-01 Updated
3 2014-12-02 Withdrawn
4 2014-12-02 New
4 2014-12-03 Updated
5 2014-12-05 New
5 2014-12-06 Updated
5 2014-12-06 Updated
5 2014-12-07 Updated
6 2014-12-08 New

First, you query doesn't need the joins -- unless you care about customers with no applications. So, this is a simpler version to get the total
select ah.CustomerId, count(*) as cnt
from applicationhistory ah
where ah.EventDate between #StartDateFilter and #EndDateFilter
group by a.CustomerId;
Note that the group by only has CustomerId and not ApplicationId.
If you want only "New" applications, the use where:
select ah.CustomerId, count(*) as cnt
from applicationhistory ah
where ah.EventDate between #StartDateFilter and #EndDateFilter and
EventType = 'New'
group by a.CustomerId;
If you want net applications "new" - "withdrawn", then use conditional aggregation:
select ah.CustomerId,
sum(case when EventType = 'New' then 1 else -1 end) as cnt
from applicationhistory ah
where ah.EventDate between #StartDateFilter and #EndDateFilter and
EventType in ( 'New', 'Withdrawn' )
group by a.CustomerId;

Related

Group by with set of values in PostgreSQL

I have a product table:
productid product
1 A-110
2 B-110
3 C-400
4 D-401
And orderditems table:
orderitemid productid qty
1 1 10
2 2 10
3 3 10
4 3 10
5 4 10
I can group by based on product as:
select productid, sum(qty)
from ordereditems
group by productid
Which gives:
1 10
2 10
3 20
4 10
However for this query productid 1 & 2 , 3 & 4 are the same.
Meaning that I want to see:
1 + 2 20
3 + 4 30
Basically I want the query to understand that 1 & 2 are the same group and 3 & 4 are the same group.
How can i do that?
Edit:
Products 1 & 2 , 3 & 4 are not the same. However they are of the same family... What I want is to see how much we sell per family and not per product.
This is why the desired out put is:
1 + 2 20
3 + 4 30
A hacky version if you know that 1&2 are the same and 3&4 are the same, and those are the only things the same.
select
case
when productid in (1,2) then '1 + 2'
when productid in (3,4) then '3 + 4'
else productid
end as product_ids, sum(qty)
from ordereditems
group by
case
when productid in (1,2) then '1 + 2'
when productid in (3,4) then '3 + 4'
else productid
end
A better approach is to record in the database that two products are the same. Either by linking the duplicated products (2,4) to the product they're the same as (1,3) or by creating a new table family etc. and recording that products 1,2 are related to the same family.
family
familyid name
1 1+2
2 3+4
Extend product table
productid product familyid
1 A-110 1
2 B-110 1
3 C-400 2
4 D-401 2
then you can group on family
select f.name, sum(qty)
from ordereditems oi
inner join product p on oi.productid = p.productid
inner join family f on p.familyid = f.familyid
group by f.name
Pls add a GroupID/Category field to your table.
productid product GroupID
1 A-110 A
2 B-110 A
3 C-400 B
4 D-401 B
Then use the query
select GroupID, sum(qty) as Sold
from ordereditems
group by GroupID
This will represent as group sale as if you have more than 10 products in a group your productid will make complicity to understand.
If it works for you pls mark as helpful.. :)

Displaying results based on customer first order after specific date SQL Server

These are my current results
CustomerID OrderID Date TableFK
1 1 2017-01-05 1
1 2 2017-01-06 1
1 3 2017-01-10 1
2 4 2017-01-10 2
2 5 2017-01-09 2
2 6 2017-01-16 2
Now basically i want to return the customer who has placed his first order on or after '2017-01-09'
If i use
WHERE Orders.Date > '2017-01-09'
I obviously get
CustomerID OrderID Date TableFK
1 3 2017-01-10 1
2 4 2017-01-10 2
2 5 2017-01-09 2
2 6 2017-01-16 2
But the output i am looking for is
CustomerID OrderID Date TableFK
2 4 2017-01-10 2
2 5 2017-01-09 2
2 6 2017-01-16 2
How can i achieve this? i have an SQL fiddle http://sqlfiddle.com/#!9/3d55d/2
Use a having clause:
select o.TableFK as CustomerId
from Orders o
group by o.TableFK
having min(o.Date) >= '2017-01-09';
(The SQL Fiddle is here.)
Note that you do not need the join to Customers because the customers you want need to have an order.
A comment on naming. The name TableFK is really meaningless. It doesn't describe the column at all. I'm a fan of having foreign keys have the same name as the primary key they are referring to, so CustomerId instead of TableFK.
You can use an inner join and where clause to do this.
Using contents of your fiddle in the question:
select o.* from orders o
inner join (
select tableFK, min(date) as firstOrder
from orders
group by tableFK ) f
on f.tableFK = o.tableFK
where f.firstOrder >= '17-01-09'
I would just run a check to see if the customer has a record in the orders table that is before your date, like this:
SELECT *
FROM Customer c
INNER JOIN Orders o
ON c.CustomerID = o.TableFK
WHERE NOT EXISTS
(SELECT 1
FROM Orders
WHERE Orders.TableFK = o.TableFK
AND Date < '2017-01-09')
This should work with the fiddle you created.

SQL/Microsoft Access

I am trying to figure out this problem but, it's already taking me a few days and I cant seem to solve it.
The questions demands:
Display a list of products (show ProductID and ProductDescription as the first two columns), the number of times each product has been ordered (the third column), and the total quantity each product has been ordered over all orders (the forth column).
The Database:
**Product_T** **OrderLine_T**
ProductID ProductDescription ProductID OrderedQuantity
1 End Table 1 2
2 Coffe Table 2 2
3 Computer Desk 4 1
4 Entertainment Center 3 5
5 Writers Desk 3 3
6 8-Drawer Desk 6 2
7 Dining Table 8 2
8 Computer Desk 4 4
4 1
5 2
7 2
1 3
2 2
3 3
8 3
4 2
7 3
8 10
Just a JOIN and GROUP BY
SELECT p.ProductID,
p.ProductDescription,
COUNT(*) AS time_ordered,
SUM(o.OrderedQuantity) AS qty_ordered
FROM Product_T as p
LEFT JOIN OrderLine_T AS o
ON p.ProductID = o.ProductID
GROUP BY p.ProductID,
p.ProductDescription;
Try this:
SELECT t1.ProductID,
t1.ProductDescription,
COALESCE(t2.num_times_ordered, 0) AS num_times_ordered,
COALESCE(t2.total_quantity, 0) AS total_quantity
FROM Product_T t1
LEFT JOIN
(
SELECT ProductID,
COUNT(*) AS num_times_ordered,
SUM(OrderedQuantity) AS total_quantity
FROM OrderLine-T
GROUP BY ProductID
) t2
ON t1.ProductID = t2.ProductID
The answer given by #GurV is more concise than this and works for this particular problem, but in general you would need to use a subquery to obtain stats from the OrderLine-T table, assuming that you wanted to include non-aggregate columns from Product_T in your report.

Remove Duplicate Records in Hive

I want to create a table that indicates medical providers that are linked by common members. For example, if I go to prov 1 and prov 2, then prov 1 and prov 2 will be linked because I visited both.
I have a table where each record indicates a member visiting a provider on a specific date. The table contains millions of members and thousands of provs. Below is a small example of the table:
member prov date
1 1 1/1/15
1 2 1/2/15
2 16 1/12/14
2 5 1/1/16
I am trying to create a table where each record indicates two distinct providers being linked by a common member. For example:
member prov1 prov2 date1 date2
1 1 2 1/1/15 1/2/15
2 16 5 1/12/14 1/1/16
I am trying to use an inner join on the same table, but it is returning duplicate records. I thought the distinct clause would fix this, but it does not seem to get the job done. My query is shown below:
select distinct a.member, a.prov, b.prov, a.date, b.date
from table1 as a
inner join table1 as b
on a.member=b.member
This query returns distinct records, but there are records that contain the same information. Below shows an example of this:
a.member a.prov b.prov a.date b.date
1 1 2 1/1/15 1/2/15
1 2 1 1/2/15 1/1/15
Above we see that the records are distinct, but they describe the same information. Below is what I want the query to return:
a.member a.prov b.prov a.date b.date
1 1 2 1/1/15 1/2/15
How can I alter the above query so that I only return distinct information? I don't want 1 record per member. I want 1 record for each distinct prov pairings by member.
One option is to use conditional aggregation with a subquery using row_number:
select member,
max(case when rn = 1 then prov end) prov1,
max(case when rn = 2 then prov end) prov2,
max(case when rn = 1 then date end) date1,
max(case when rn = 2 then date end) date2
from (select member,
prov,
date,
row_number() over (partition by member order by prov, date) rn
from table1) t
group by member

Oracle SQL: Insert based on found value in a column given condition from another table

I want to merge my order data into one table which is now in two separate tables:
Order ID and customer code in table Orders:
Order_ID Customer
1 C11
2 C76
4 C32
and order detalis in table Details (with columns Order_ID, Hour, Quantity) in which the ordered quantity for the hours that the order is valid is given:
Order_ID Hour Quantity
1 2 10
1 3 20
2 2 5
2 3 5
2 4 5
4 6 20
4 7 25
I want to merge data of these two tables in one table to have only one row per each order by inserting the quantity for the hours that the order is valid in corresponding column, otherwise zero.
Order_ID Cutomer Hour1 Hour2 Hour3 Hour4 Hour5 Hour6 Hour7 ...
1 C11 0 10 20 0 0 0 0
2 C76 0 5 5 5 0 0 0
4 C32 0 0 0 0 0 20 25
I tried (only for quantity of hour 1):
insert into Merged_Order_Table
(Order_ID,Customer,Hour1)
select
Orders.Order_id,Orders.Customer,
case
when 1 in (select Details.Hour from Details,Orders where
Details.Order_ID = Orders.Order_ID)
then Details.Quantity
else 0
end
from
Orders
inner join
Details
on
Details.Order_ID = Orders.Order_ID;
But got quantity in Hour1 even for orders with no quantity in this hour.
I question why you would want to take nicely normalized data and put it into a table with that structure. I can understand a query returning the data like that, but another table?
In any case, your problem is a common problem when using correlated subqueries. The table being correlated is included in the subquery. Ooops. Here is fix for that:
insert into Merged_Order_Table(Order_ID, Customer, Hour1)
select o.Order_id, o.Customer,
(case when 1 in (select d.Hour from Details d where d.Order_ID = o.Order_ID)
then d.Quantity
else 0
end)
from Orders o;
That said, what you really want is conditional aggregation:
insert into Merged_Order_Table(Order_ID, Customer, Hour1)
select o.Order_id, o.Customer,
sum(case when d.Hour = 1 then d.Quantity else 0 end)
from Orders o left join
Details d
on o.Order_ID = d.Order_ID
group by o.Order_id, o.Customer;
You are on a right track!
you just need one more layer:
select order_id, customer, max(quantity) hour1 from
(your query)
group by order_id, customer
Or you can look into how to do PIVOT tables