TSQL grouping/select help - tsql

Hi all wonder if someone can lend a hand; i've got this tsql script (shown below) that is currently returning data based on the owner id, if the record is active and if the record created date is less than todays date. I am then grouping the data together. What i want to achieve is return the most recent record per company.
Currently the data i return is this:
COMPANY A JOE BLOGS NULL 10088 Green NULL NULL 21/07/2007 16:57 Phone Call
COMPANY B JOE BLOGS NULL 10059 Green NULL NULL 20/07/2007 14:57 Phone Call
COMPANY B JOE BLOGS NULL 10059 Green NULL NULL 18/07/2006 09:47 E-mail
COMPANY B JOE BLOGS NULL 10059 Green NULL NULL 19/07/2006 13:19 E-mail
COMAPANY C JOE BLOGS NULL 10866 Green NULL NULL 17/08/2007 12:57 Phone Call
COMAPANY C JOE BLOGS NULL 10866 Green NULL NULL 13/08/2007 10:59 E-mail
COMAPANY C JOE BLOGS NULL 10866 Green NULL NULL 15/08/2007 14:57 E-mail
This is how i want the data to return:
COMPANY A JOE BLOGS NULL 10088 Green NULL NULL 21/07/2007 16:57 Phone Call
COMPANY B JOE BLOGS NULL 10059 Green NULL NULL 20/07/2007 14:57 Phone Call
COMAPANY C JOE BLOGS NULL 10866 Green NULL NULL 17/08/2007 12:57 Phone Call
Could someone, point me in the right direction please?
SELECT fa.name, fa.owneridname, fa.new_technicalaccountmanageridname, fa.new_customerid, fa.new_riskstatusname,
fa.new_numberofopencases, fa.new_numberofurgentopencases, fap.actualend, fap.activitytypecodename, fap.createdby, fap.createdbyname
FROM FilteredAccount fa
INNER JOIN FilteredActivityPointer fap ON fa.accountid = fap.regardingobjectid
WHERE fa.statecodename = 'Active'
AND fap.ownerid = '0F995BDC'
AND fap.createdon < getdate()
GROUP BY fa.name, fa.owneridname, fa.new_technicalaccountmanageridname, fa.new_customerid, fa.new_riskstatusname,
fa.new_numberofopencases, fa.new_numberofurgentopencases, fap.actualend, fap.activitytypecodename, fap.createdby, fap.createdbyname

Try this
SELECT * FROM (
SELECT fa.name, fa.owneridname, fa.new_technicalaccountmanageridname, fa.new_customerid, fa.new_riskstatusname,
fa.new_numberofopencases, fa.new_numberofurgentopencases, fap.actualend, fap.activitytypecodename, fap.createdby, fap.createdbyname ,
RN = ROW_NUMBER() OVER (PARTITION BY fa.name ORDER BY fap.createdby DESC)
FROM FilteredAccount fa
INNER JOIN FilteredActivityPointer fap ON fa.accountid = fap.regardingobjectid
WHERE fa.statecodename = 'Active'
AND fap.ownerid = '0F995BDC'
AND fap.createdon < getdate()
) a WHERE RN = 1

Related

Require result with of multiple customers along with city name

we have following requirement
I need customer_name which are in Active status and attribute_name=City and attribute_value in(Indore,Mumbai) and result should return count less than=2
It means from result for Indore i should get 2 results out of 3 and for Mumbai i should get 1 out of 1.
I tried below 2 ways but getting all the rows for customers which are in city Indore and Mumbai
Customer_table has below details
customer_id customer_name customer_Status
------------------------------------------
1 ABC Active
2 XYZ Active
3 PQR NA
4 ABCD Active
4 ABCDE Active
customer_details table has below details
customer_id attribute_name attribute_value
------------------------------------------
1 City Indore
1 Phone Number 9100000000
1 Country India
2 City Mumbai
2 Phone Number 9100000001
2 Country India
3 City Delhi
3 Phone Number 9100000002
3 Country India
4 City Mumbai
4 Phone Number 9100000003
4 Country India
5 City Mumbai
5 Phone Number 9100000004
5 Country India
Code:-
select attribute_value, r.customer_name from customer_details res
join lateral (
select customer_name from Customer_table
where res.customer_id=customer_id
and customer_Status= 'Active'
limit 2
) r on true
where attribute_name= 'City' and attribute_value in ('Indore','Mumbai');
Code:-
SELECT s.customer_name,attribute_value
FROM (
SELECT *, row_number() OVER (PARTITION BY customer_id ) AS rn
FROM customer_details
WHERE attribute_name= 'City' and attribute_value in ('Indore','Mumbai')
) e
JOIN Customer_table s USING (customer_id)
WHERE rn <= 2
and and customer_Status= 'Active'
ORDER BY customer_id, e.rn;
Please try this way.
select customer_name,attribute_value from (
select ct.customer_name,attribute_value,row_number() OVER (PARTITION BY attribute_value ) AS rn
from customer_table ct ,customer_details cd
where ct.customer_status = 'Active' and ct.customer_id = cd.customer_id
and attribute_name='City' and attribute_value in('Indore','Mumbai')
) as t
where rn <= 2
This design will kill any performance you hope to get around customers. The first thing I would do in drop the customer_details table and the columns phone_number, city, and country to the customers table. Lacking that I would create a view that joined customers to customer_details having all the columns in the view.
create view Customer_Standard as
select c.cust_id, c.name, c.status, ph.attribute_value phone_number, ct.attribute_value city, cn.attribute_value country
from customers c
left join customer_details ph on (ph.cust_id = c.cust_id and ph.attribute_name = 'Phone Number')
left join customer_details ct on (ct.cust_id = c.cust_id and ct.attribute_name = 'City')
left join customer_details cn on (cn.cust_id = c.cust_id and cn.attribute_name = 'Country') ;
Then the query you want becomes:
select cust_id, name, status, phone_number, city, country
from (select cs.*, row_number() over (partition by city order by cust_id) rn
from Customer_Standard cs
) cust
where city in ('Indore','Mumbai')
and rn<3;

SQL Server recursive query with left outer join

I have two tables Customers and Orders with some data.
SELECT * FROM Customers C;
Result:
CustomerId Name
--------------------
1 Shree;
2 Kalpana;
3 Basavaraj;
Query:
select * from Orders O;
Result:
OrderId CustomerId OrderDate
-------------------------------------------------
100 1 2017-01-05 23:16:15.497
200 4 2017-01-06 23:16:15.497
300 3 2017-01-07 23:16:15.497
I have a business requirement where i need to populate data from Customers left outer join Orders in repeated way. I have written below query and desired data.
SELECT *
FROM Customers C
LEFT OUTER JOIN
(SELECT *
FROM Orders
WHERE OrderId = 100) O ON O.CustomerId = C.CustomerId
UNION ALL
SELECT *
FROM Customers C
LEFT OUTER JOIN
(SELECT *
FROM Orders
WHERE OrderId = 200) O ON O.CustomerId = C.CustomerId
UNION ALL
SELECT *
FROM Customers C
LEFT OUTER JOIN
(SELECT *
FROM Orders
WHERE OrderId = 300) O ON O.CustomerId = C.CustomerId;
Desired Result:
CustomerId Name OrderId CustomerId OrderDate
--------------------------------------------------------------------
1 Shree 100 1 2017-01-05 23:16:15.497
2 Kalpana NULL NULL NULL
3 Basavaraj NULL NULL NULL
1 Shree NULL NULL NULL
2 Kalpana NULL NULL NULL
3 Basavaraj NULL NULL NULL
1 Shree NULL NULL NULL
2 Kalpana NULL NULL NULL
3 Basavaraj 300 3 2017-01-07 23:16:15.497
I have one option to put left outer query in loop and pass the OrderId and finally save the result data but that takes lots of time because of high number of records. I want to know the best way to get this done. I have tried function and CTE but no luck so far. Please help.
Many thanks in advance.
A cartesian product can do the job:
SELECT C.*,
OrderId = CASE WHEN C.CustomerId = O.CustomerID THEN O.OrderId ELSE NULL END,
CustomerId = CASE WHEN C.CustomerId = O.CustomerID THEN O.CustomerId ELSE NULL END,
OrderDate = CASE WHEN C.CustomerId = O.CustomerID THEN O.OrderDate ELSE NULL END
FROM Orders O, Customers C
I have got the solution using similar to Cartesian product. Store the CustomerId in table variable and than make Cartesian production with same. This works as i wanted.
declare #CustomerTable TABLE (ID int IDENTITY(1,1) NOT NULL, CustomerId int);
insert into #CustomerTable select distinct CustomerId from orders;
select v.ID,isnull(v.CT_CustomerId,o.CustomerId) as CT_CustomerId,v.CustomerId,v.Name,o.* from
(select CT.ID,CT.CustomerId as CT_CustomerId,C.CustomerId,C.Name from #CustomerTable CT,Customers C ) V
left outer join Orders O ON O.CustomerId = V.CustomerId and V.ID=o.ID

T-SQL : How to obtain the last modified row from a grouping

I'm working with a database that have a poor design that does not constraint duplicates rows as long as they have a different unique-identifier.
Within one of the table, a given user can have an attribute and a value for the attribute. Normally, a user would only a have a single time the attribute but because of the poor design, I'm getting a lot of duplicates in the table and now I need to clean that mess. This is due to the CRM software not always checking if the row exists when we modify the employee profile but instead it creates a bunch of new rows with duplicates values.
The following query returns the duplicates values:
SELECT ua.ID AS LineID
,ua.Modified AS LineLastModifiedDate
,u.FullName AS EmployeeName
,a.Name AS AttributeName
,ua.value AS AttributeValue
FROM UserAttributes AS ua
INNER JOIN Users AS u ON ua.userid = u.id
INNER JOIN Attributes AS a ON ua.AttributeID = a.ID
WHERE EXISTS (
SELECT NULL
FROM UserAttributes as ua2
WHERE ua2.UserID = ua.UserID
AND ua2.AttributeID = ua.AttributeID
AND ua2.ID != ua.ID
)
And produces results as this:
LineID LineLastModifiedDate EmployeeName AttributeName AttributeValue
------ ----------------------- ------------- --------------- ---------------
15 2016-01-01 Employee1 EmployeeNumber 15
19 2016-07-20 Employee1 EmployeeNumber 15
35 2016-01-01 Employee2 EmployeeSex M
96 2016-07-20 Employee2 EmployeeSex M
21 2016-03-03 Employee1 SickDays 3
99 2016-07-10 Employee1 SickDays 5
What I need to accomplish starting from this query is : ForEach grouping of the same EmployeeName and AttributeName, give me the last modified line expecting results like this :
LineID LineLastModifiedDate EmployeeName AttributeName AttributeValue
------ ----------------------- ------------- --------------- ---------------
19 2016-07-20 Employee1 EmployeeNumber 15
96 2016-07-20 Employee2 EmployeeSex M
99 2016-07-10 Employee1 SickDays 5
How can I modify my query to accomplish this ?
Thank you
-M
;WITH CTE
AS
(
SELECT ua.ID AS LineID
,ua.Modified AS LineLastModifiedDate
,u.FullName AS EmployeeName
,a.Name AS AttributeName
,ua.value AS AttributeValue
,ROW_NUMBER() OVER (PARTITION BY EMPLOYEENAME,EMPLOYEESEX ORDER BY UA.Modified DESC) AS RN
FROM UserAttributes AS ua
INNER JOIN Users AS u ON ua.userid = u.id
INNER JOIN Attributes AS a ON ua.AttributeID = a.ID
WHERE EXISTS (
SELECT NULL
FROM UserAttributes as ua2
WHERE ua2.UserID = ua.UserID
AND ua2.AttributeID = ua.AttributeID
AND ua2.ID != ua.ID
)
)
SELECT * FROM cte where rn=1
You can use row numbering or a scheme as below where you pull out the max value and then use a join. Presumably you can't have ties by date.
select ...
from
UserAttributes as ua
inner join
(
select
UserID, AttributeID,
max(LineLastModifiedDate) as MaxLineLastModifiedDate
fromUserAttributes
group by UserId
) as max_ua
on max_ua.UserID = ua.UserID
and max_ua.AttributeID = max_ua.AttributeID
and max_ua.MaxLineLastModifiedDate = ua.LineLastModifiedDate
...

Redshift PostgreSQL Distinct ON Operator

I have a data set that I want to parse for to see multi-touch attribution. The data set is made up by leads who responded to a marketing campaign and their marketing source.
Each lead can respond to multiple campaigns and I want to get their first marketing source and their last marketing source in the same table.
I was thinking I could create two tables and use a select statement from both.
The first table would attempt to create a table with the most recent marketing source from every person (using email as their unique ID).
create table temp.multitouch1 as (
select distinct on (email) email, date, market_source as last_source
from sf.campaignmember
where date >= '1/1/2016' ORDER BY DATE DESC);
Then I would create a table with deduped emails but this time for the first source.
create table temp.multitouch2 as (
select distinct on (email) email, date, market_source as first_source
from sf.campaignmember
where date >= '1/1/2016' ORDER BY DATE ASC);
Finally I wanted to simply select the email and join the first and last market sources to it each in their own column.
select a.email, a.last_source, b.first_source, a.date
from temp.multitouch1 a
left join temp.multitouch b on b.email = a.email
Since distinct on doesn't work on redshift's postgresql version I was hoping someone had an idea to solve this issue in another way.
EDIT 2/22: For more context I'm dealing with people and campaigns they've responded to. Each record is a "campaign response" and every person can have more than one campaign response with multiple sources. I'm trying make a select statement which would dedupe by person and then have columns for the first campaign/marketing source they've responded to and the last campaign/marketing source they've responded to respectively.
EDIT 2/24: Ideal output is a table with 4 columns: email, last_source, first_source, date.
The first and last source columns would be the same for people with only 1 campaign member record and different for everyone who has more than 1 campaign member record.
I believe you could use row_number() inside case expressions like this:
SELECT
email
, MIN(first_source) AS first_source
, MIN(date) first_date
, MAX(last_source) AS last_source
, MAX(date) AS last_date
FROM (
SELECT
email
, date
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date ASC) = 1 THEN market_source
ELSE NULL
END AS first_source
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date DESC) = 1 THEN market_source
ELSE NULL
END AS last_source
FROM sf.campaignmember
WHERE date >= '2016-01-01'
) s
WHERE first_source IS NOT NULL
OR last_source IS NOT NULL
GROUP BY
email
tested here: SQL Fiddle
PostgreSQL 9.3 Schema Setup:
CREATE TABLE campaignmember
(email varchar(3), date timestamp, market_source varchar(1))
;
INSERT INTO campaignmember
(email, date, market_source)
VALUES
('a#a', '2016-01-02 00:00:00', 'x'),
('a#a', '2016-01-03 00:00:00', 'y'),
('a#a', '2016-01-04 00:00:00', 'z'),
('b#b', '2016-01-02 00:00:00', 'x')
;
Query 1:
SELECT
email
, MIN(first_source) AS first_source
, MIN(date) first_date
, MAX(last_source) AS last_source
, MAX(date) AS last_date
FROM (
SELECT
email
, date
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date ASC) = 1 THEN market_source
ELSE NULL
END AS first_source
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date DESC) = 1 THEN market_source
ELSE NULL
END AS last_source
FROM campaignmember
WHERE date >= '2016-01-01'
) s
WHERE first_source IS NOT NULL
OR last_source IS NOT NULL
GROUP BY
email
Results:
| email | first_source | first_date | last_source | last_date |
|-------|--------------|---------------------------|-------------|---------------------------|
| a#a | x | January, 02 2016 00:00:00 | z | January, 04 2016 00:00:00 |
| b#b | x | January, 02 2016 00:00:00 | x | January, 02 2016 00:00:00 |
& a small extension to the request, count the number of contact points.
SELECT
email
, MIN(first_source) AS first_source
, MIN(date) first_date
, MAX(last_source) AS last_source
, MAX(date) AS last_date
, MAX(numof) AS Numberof_Contacts
FROM (
SELECT
email
, date
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date ASC) = 1 THEN market_source
ELSE NULL
END AS first_source
, CASE
WHEN ROW_NUMBER() OVER (PARTITION BY email ORDER BY date DESC) = 1 THEN market_source
ELSE NULL
END AS last_source
, COUNT(*) OVER (PARTITION BY email) as numof
FROM campaignmember
WHERE date >= '2016-01-01'
) s
WHERE first_source IS NOT NULL
OR last_source IS NOT NULL
GROUP BY
email
You can use the good old left join groupwise maximum.
SELECT DISTINCT c1.email, c1.date, c1.market_source
FROM sf.campaignmember c1
LEFT JOIN sf.campaignmember c2
ON c1.email = c2.email AND c1.date > c2.date AND c1.id > c2.id
LEFT JOIN sf.campaignmember c3
ON c1.email = c3.email AND c1.date < c3.date AND c1.id > c3.id
WHERE c1.date >= '1/1/2016' AND c2.date >= '1/1/2016'
AND (c2.email IS NULL OR c3.email IS NULL)
This assumes you have an unique id column, if (date, email) is unique id is not needed.

How to get the Customer Detail + whether he has (an) order or not

I have 2 tables. Customers and Orders.
My requirement is...
I would like to get the result like the following
Customer Detail + HasOrders + Count(Orders)
I wrote
SELECT Customers.*
, CASE WHEN o.CustomerID IS NOT NULL THEN 1 ELSE 0 END HasOrders
FROM Customers c
LEFT JOIN Orders o
ON c.CustomerID = o.CustomersID
But it returns many rows. If the customer has 5 orders, it returns 5 rows for each Customer.
Could you please advise me? Thanks.
You need to do the counting in derived table.
SELECT c.*
, case when o.CustomerID is not null
then 1
else 0
end HasOrders
, o.NumberOfOrders
FROM Customers c
LEFT JOIN
(
SELECT CustomerID
, count(*) NumberOfOrders
FROM Orders
GROUP BY CustomerID
) o
ON c.CustomerID = o.CustomersID