Selecting customer IDs who have purchased from a certain channel - sql

I'm trying to pull a list of customer IDs who have purchased online only and another list of those who have purchased both online and in-store (no customers for in-store only). I currently have a table that looks like this:
-------------------------------------------------------
**customerid** **shipped_dt** **channel**
1 2018-10-31 online
1 2018-11-01 store
2 2018-11-01 store
3 2018-11-01 online
3 2018-11-02 online
In this case, for the list with those who have purchased both online and in-store, I was customerid 1. For the list of customers that are online only, I want customerid 3. How exactly would I go about writing a code for this? I'm still learning SQL so I'm not too knowledgeable about the proper syntax and abilities within SQL.
I only want a return of customerid and the channel that they purchased through.
Thank you!

If you have a separate list of customers, you might want to use exists:
For example, to get customers who have purchased in both places:
select c.*
from customers c
where exists (select 1 from t where t.customer_id = c.customer_id and t.channel = 'online'
) and
exists (select 1 from t where t.customer_id = c.customer_id and t.channel = 'store'
) ;
This can have some advantages over aggregation:
exists and not exists can make direct use of an index on (customer_id, store).
You can get customers who purchased in neither channel.
You can get additional information about the customer, if that is desirable.

Just a minor alternative
Example
Select customerid
,Channel = min(Channel)
From YourTable
Group By customerid
Having min(Channel)=max(channel)
and min(Channel)='online'
Note: If you remove and min(Channel)='online' you would see customers who purchased through only one channel
Returns
customerid Channel
3 online

For the case of 'online' only customers use not exists to exclude those who have purchased in 'store':
select distinct customerid
from tablename t
where not exists (
select 1 from tablename where customerid = t.customerid and channel = 'store'
)
For the case of 'online' and 'store':
select distinct customerid
from tablename t
where
(select count(distinct channel) from tablename where customerid = t.customerid) = 2
You can combine the above queries into this:
select distinct customerid, 'online' channel
from tablename t
where not exists (
select 1 from tablename where customerid = t.customerid and channel = 'store'
)
union all
select distinct customerid, 'both' channel
from tablename t
where
(select count(distinct channel) from tablename where customerid = t.customerid) = 2
See the demo

Related

Trouble finding which customer bought iphone or HTC using SQL script

I need to find which customer bought phones. If the customer bought iPhone only or iPhone and HTC then I need data for iPhone only. But if the customer bought only HTC I would need data for HTC.
Original Data
Expected output table
My code does not work and I'm not sure how to use the where clause. Could you please direct me on where I'm making a mistake?
Below is my code:
select Cust_ID, Year, Month, Product from custTable where Item_ID = (
Case
when Item_ID in ('iPhone','HTC') then 'iPhone'
else 'HTC'
End )
You can use not exists:
select o.*
from original o
where o.product = 'iPhone' or
(o.product = 'HTC' and
not exists (select 1
from original o2
where o2.cust_id = o.cust_id and
o2.product = 'iPhone'
)
);
This is almost a direct translation of your conditions.
Try this:
select v1.Cust_ID,
v1.Year,
v1.Month,
substring(min(Product),4,1000) as Product
from (
select Cust_ID,
Year,
Month,
case when Product in ('iPhone') then '1. iPhone'
when Product in ('HTC') then '2. HTC'
else Product
end as Product
from custTable
) v1
group by v1.Cust_ID,
v1.Year,
v1.Month
Or this:
select v1.Cust_ID,
v1.Year,
v1.Month,
v1.Product
from (
select Cust_ID,
Year,
Month,
Product,
ROW_NUMBER() over(partition by Cust_ID, Year, Month order by case when Product = 'iPhone' then 1 when Product = 'HTC' then 2 else 999 end) as rn
from custTable
) v1
where v1.rn = 1

sql program with Group by 2 attributes and 1 condition (Import)

I have a text file that contains Candy issued data.
File contains customer id, issue date, candy name.
C1,2014-01-01,Candy1
C1,2014-01-02,Candy2
C2,2002-06-01,Candy2
C1,2014-01-02,Candy3
C2,2002-06-01,Candy3
I am trying to figure out how can I write a program that creates a list of pairs of candies which were issued together i.e. issued to the same customer on the same day at least twenty-five different times.
Thanks in advance.
--create a table to hold our data
create table CandySales
(
CustomerId nchar(2)
, SaleDate Date
, CandyId nvarchar(10)
)
--upload the data from the csv
bulk insert CandySales
from 'c:\temp\myCsv.csv'
with (fieldterminator = ',', rowterminator = '\n')
--query the data
;with cte as
(
select customerId, candyId, saleDate
from CandySales
group by customerId, candyId, saleDate
having COUNT(1) >= 25
)
select distinct
a.CandyId Item1
, b.CandyId Item2
from cte a
inner join cte b
on a.CandyId > b.CandyId
and a.SaleDate = b.SaleDate
and a.CustomerId = b.CustomerId
Explanation of Query
The cte creates a list of all candies which have had 25 or more sales to the same customer on the same day.
include distinct as we may have the same pair returned for multiple dates/customers; we only want each pair once.
on a.CandyId > b.CandyId is used as we want to ensure the pair contains different candies from one another. We use > instead of != to avoid getting the same pair with item1 and item2 reversed.
we then join on sale date and customer date as we want items which were sold to the same customer on the same day.

SQL: multiple counts from same table

I am having a real problem trying to get a query with the data I need. I have tried a few methods without success. I can get the data with 4 separate queries, just can't get hem into 1 query. All data comes from 1 table. I will list as much info as I can.
My data looks like this. I have a customerID and 3 columns that record who has worked on the record for that customer as well as the assigned acct manager
RecID_Customer___CreatedBy____LastUser____AcctMan
1-------1374----------Bob Jones--------Mary Willis------Bob Jones
2-------1375----------Mary Willis------Bob Jones--------Bob Jones
3-------1376----------Jay Scott--------Mary Willis-------Mary Willis
4-------1377----------Jay Scott--------Mary Willis------Jay Scott
5-------1378----------Bob Jones--------Jay Scott--------Jay Scott
I want the query to return the following data. See below for a description of how each is obtained.
Employee___Created__Modified__Mod Own__Created Own
Bob Jones--------2-----------1---------------1----------------1
Mary Willis------1-----------2---------------1----------------0
Jay Scott--------2-----------1---------------1----------------1
Created = Counts the number of records created by each Employee
Modified = Number of records where the Employee is listed as Last User
(except where they created the record)
Mod Own = Number of records for each where the LastUser = Acctman
(account manager)
Created Own = Number of Records created by the employee where they are
the account manager for that customer
I can get each of these from a query, just need to somehow combine them:
Select CreatedBy, COUNT(CreatedBy) as Created
FROM [dbo].[Cust_REc] GROUP By CreatedBy
Select LastUser, COUNT(LastUser) as Modified
FROM [dbo].[Cust_REc] Where LastUser != CreatedBy GROUP By LastUser
Select AcctMan, COUNT(AcctMan) as CreatePort
FROM [dbo].[Cust_REc] Where AcctMan = CreatedBy GROUP By AcctMan
Select AcctMan, COUNT(AcctMan) as ModPort
FROM [dbo].[Cust_REc] Where AcctMan = LastUser AND NOT AcctMan = CreatedBy GROUP By AcctMan
Can someone see a way to do this? I may have to join the table to itself, but my attempts have not given me the correct data.
The following will give you the results you're looking for.
select
e.employee,
create_count=(select count(*) from customers c where c.createdby=e.employee),
mod_count=(select count(*) from customers c where c.lastmodifiedby=e.employee),
create_own_count=(select count(*) from customers c where c.createdby=e.employee and c.acctman=e.employee),
mod_own_count=(select count(*) from customers c where c.lastmodifiedby=e.employee and c.acctman=e.employee)
from (
select employee=createdby from customers
union
select employee=lastmodifiedby from customers
union
select employee=acctman from customers
) e
Note: there are other approaches that are more efficient than this but potentially far more complex as well. Specifically, I would bet there is a master Employee table somewhere that would prevent you from having to do the inline view just to get the list of names.
this seems pretty straight forward. Try this:
select a.employee,b.created,c.modified ....
from (select distinct created_by from data) as a
inner join
(select created_by,count(*) as created from data group by created_by) as b
on a.employee = b.created_by)
inner join ....
This highly inefficient query may be a rough start to what you are looking for. Once you validate the data then there are things you can do to tidy it up and make it more efficient.
Also, I don't think you need the DISTINCT on the UNION part because the UNION will return DISTINCT values unless UNION ALL is specified.
SELECT
Employees.EmployeeID,
Created =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.CreatedBy=Employees.EmployeeID),
Mopdified =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.LastUser=Employees.EmployeeID AND Cust_REc.CreateBy<>Employees.EmployeeID),
ModOwn =
CASE WHEN NOT Empoyees.IsManager THEN NULL ELSE
(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID)
END,
CreatedOwn=(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID AND CReatedBy=Employees.EMployeeID)
FROM
(
SELECT
EmployeeID,
IsManager=CASE WHEN EXISTS(SELECT AcctMan FROM CustRec WHERE AcctMan=EmployeeID)
FROM
(
SELECT DISTINCT
EmployeeID
FROM
(
SELECT EmployeeID=CreatedBy FROM Cust_Rec
UNION
SELECT EmployeeID=LastUser FROM Cust_Rec
UNION
SELECT EmployeeID=AcctMan FROM Cust_Rec
)AS Z
)AS Y
)
AS Employees
I had the same issue with the Modified column. All the other columns worked okay. DCR example would work well with the join on an employees table if you have it.
SELECT CreatedBy AS [Employee],
COUNT(CreatedBy) AS [Created],
--Couldn't get modified to pull the right results
SUM(CASE WHEN LastUser = AcctMan THEN 1 ELSE 0 END) [Mod Own],
SUM(CASE WHEN CreatedBy = AcctMan THEN 1 ELSE 0 END) [Created Own]
FROM Cust_Rec
GROUP BY CreatedBy

SQL Query to evaluate multiple rows

I would like to retrieve only the customers from an order table who have paid for all the orders. (Paid = 'Y').
The order table looks like this:
Cus # Order # Paid
111 1 Y
111 2 Y
222 3 Y
222 4 N
333 5 N
In this example the query should only return customer 111.
The query
Select * from order where Paid = 'Y';
returns customers that have paid and unpaid orders (ex. customer 222) in addition to customers who have paid for all of their orders (customer 111).
How do I structure the query to evaluate all the orders for a customer and only return information for a customer that has paid for all the orders?
Looking the problem a different way, you need only the customers who don't have any unpaid order.
sel cus from order group by Cus having min(Paid) = 'Y';
The above query also utilizes the fact that 'Y' > 'N'.
SQL Fiddle:
http://sqlfiddle.com/#!4/f6022/1
If you need to select all different orders for eligible customers, you may use OLAP functions:
select cus,order,paid from (select cus,order,paid,min(paid)
over (partition by cus)minz from order)dt where minz='Y';
With Oracle, you can also do select customer from your_table where paid='Y' minus select customer from your_table where paid='N', although I don't know if this is faster and, of course, you don't get the other fields in this case.
Don't know Oracle but often work in SQL Server, I would write query something like this
Select Cus from order group by Cus having count(*) = sum(case when Paid = ‘Y’ then 1 else 0);
Basically you retrieve customers where total order count equals to sum or paid orders.
Again, apologize for not giving proper Oracle syntax but hopefully that will point you to the right direction.
SELECT *
FROM orders oo
WHERE NOT EXISTS
(
SELECT 1
FROM orders io
WHERE oo.cus# = io.cus#
AND io.paid = 'N'
)
;

Display full records of duplicate entries across multiple columns

I have a table (customers) that includes fields telephone_1, telephone_2, telephone_3 & telephone_4.
What I need to see is full records of where any telephone numbers are used in other customers - as I believe something has gone wrong with the data somewhere and customer numbers are duplicated into each other!
I have tried the below code, but this doesn't give me what I want as only compares 1/1, 2/2, 3/3 & 4/4. I have an ID field which can be used to differentiate between records.
SELECT *
FROM Customers AS a
WHERE 1 < (SELECT Count(*)
FROM Customers AS b
WHERE a.Telephone_1 = b.Telephone_1
OR a.Telephone_2 = b.Telephone_2
OR a.Telephone_3 = b.Telephone_3
OR a.Telephone_4 = b.Telephone_4
Any assistance is appreciated - thanks!
How about this?
SELECT *
FROM Customers AS a
WHERE 1 < (
SELECT Count(*)
FROM Customers AS b
WHERE a.Telephone_1 IN (b.Telephone_1, b.Telephone_2, b.Telephone_3, b.Telephone_4)
OR a.Telephone_2 IN (b.Telephone_1, b.Telephone_2, b.Telephone_3, b.Telephone_4)
OR a.Telephone_3 IN (b.Telephone_1, b.Telephone_2, b.Telephone_3, b.Telephone_4)
OR a.Telephone_4 IN (b.Telephone_1, b.Telephone_2, b.Telephone_3, b.Telephone_4)
)
========Take 2=======
Following the comments:
Create a new table with telephone numbers nd customer IDs
CREATE TABLE tempTelephoneNos (
INTEGER customer,
VARCHAR(32) telephoneNo
);
assuming those are the appropriate data types for the customer Id and phone no.
Populate the new table
INSERT INTO tempTelephoneNos (customer, telephoneNo)
SELECT customer_id, telephone_1
FROM customers
UNION ALL
SELECT customer_id, telephone_2
FROM customers
UNION ALL
SELECT customer_id, telephone_3
FROM customers
UNION ALL
SELECT customer_id, telephone_4
FROM customers
Then you can find out which telephone numbers appear for more than one customer with
SELECT customer, telephoneNo
FROM tempTelephoneNos
WHERE 1 < (SELECT COUNT(*) FROM tempTelephoneNos GROUP BY telephoneNo)