select min booking date by each customer - sql

I have 2 tables, CustomerMaster, EntBookings, now for each customer, I have record of each of their booking(s) in EntBookings table.
What I want is, to find out the first date of booking for all customers.
What I tried,
SELECT cm.CustomerCode,
cm.CustomerName,
MIN(eb.BookingDate) OVER(PARTITION BY eb.BookingByCustomer)
FROM EntBookings eb
INNER JOIN CustomerMaster cm
ON cm.CustomerCode = eb.BookingByCustomer
GROUP BY
cm.CustomerCode,
cm.CustomerName,
eb.BookingByCustomer,
eb.BookingDate
It gives the result as
Cust1 ABC 2013-01-24 00:00:00.000
Cust2 ABCD 2013-01-24 00:00:00.000
Cust2 ABCD 2013-01-24 00:00:00.000
Cust3 BCD 2013-01-25 00:00:00.000
Cust4 BCDE 2013-01-24 00:00:00.000
Cust7 DEF 2013-01-02 00:00:00.000
I don't know why for Cust2, its showing 2 rows? How can I get it to return just one row?
If I do this..
SELECT cm.CustomerCode,
cm.CustomerName,
MIN(eb.BookingDate) OVER(PARTITION BY eb.BookingDate)
FROM EntBookings eb
INNER JOIN CustomerMaster cm
ON cm.CustomerCode = eb.BookingByCustomer
GROUP BY
cm.CustomerCode,
cm.CustomerName,
eb.BookingByCustomer,
eb.BookingDate
I get this...
Cust7 DEF 2013-01-02 00:00:00.000
Cust1 ABC 2013-01-24 00:00:00.000
Cust2 ABCD 2013-01-24 00:00:00.000
Cust4 BCDE 2013-01-24 00:00:00.000
Cust3 BCD 2013-01-25 00:00:00.000
Cust2 ABCD 2013-02-13 00:00:00.000
In this case, though I am still getting 2 rows for Cust2, dates are wrong, it should be 2013-01-24 00:00:00.000 (as in first case), but that is returning 2 rows. So, both of these queries are incorrect. Can anyone help me figure out what I am doing wrong?

You are complicating things and you are grouping too much. It seems you only want to group on customer code and name.
SELECT
cm.CustomerCode,
cm.CustomerName,
MIN(eb.BookingDate)
FROM
EntBookings eb
JOIN
CustomerMaster cm ON
cm.CustomerCode = eb.BookingByCustomer
GROUP BY
cm.CustomerCode, cm.CustomerName

Related

SQL Server: adding a calculated column for end date

I have a data set continuously being updated with date values that looks like this:
Part Number Product Status Phase1 Phase2 Phase3 Phase4 Phase5 Phase6
FD 2000 Maintain 2020-01-03 00:00:00.000 2020-01-08 00:00:00.000 2020-01-15 00:00:00.000 2020-01-17 00:00:00.000 2020-01-22 00:00:00.000 2020-01-31 00:00:00.000
FD 2001 Maintain 2020-01-03 00:00:00.000 2020-01-08 00:00:00.000 2020-01-15 00:00:00.000 2020-01-17 00:00:00.000 2020-01-22 00:00:00.000 2020-01-31 00:00:00.000
Basically, the table is made up of a part number and it's respective status and life cycle dates.
Then, I utilize CROSS APPLY to get transpose the different phase columns into one column. Here is the SQL syntax:
SELECT [Part Number], [Product Status], Phase, Date
FROM PLCMexample
CROSS APPLY (VALUES ('Phase1', [Phase1]),
('Phase2', [Phase2]),
('Phase3', [Phase3]),
('Phase4', [Phase4]),
('Phase5', [Phase5]),
('Phase6', [Phase6]))
CrossApplied (Phase, Date)
GO
This generates this view:
Part Number Product Status Phase Date
FD 2000 Maintain Registration Initiation 2020-01-03 00:00:00.000
FD 2000 Maintain Product Launch 2020-01-08 00:00:00.000
FD 2000 Maintain Phase Out 2020-01-15 00:00:00.000
FD 2000 Maintain Last Order 2020-01-17 00:00:00.000
FD 2000 Maintain Last Shipment 2020-01-22 00:00:00.000
FD 2000 Maintain Last Service 2020-01-31 00:00:00.000
FD 2001 Maintain Registration Initiation 2020-01-03 00:00:00.000
FD 2001 Maintain Product Launch 2020-01-08 00:00:00.000
FD 2001 Maintain Phase Out 2020-01-15 00:00:00.000
FD 2001 Maintain Last Order 2020-01-17 00:00:00.000
FD 2001 Maintain Last Shipment 2020-01-22 00:00:00.000
FD 2001 Maintain Last Service 2020-01-31 00:00:00.000
Which is all well and good, but now I need not only just a Date column for the view, but StartDate and EndDate. The StartDate would be the values in the current Date column. The EndDate would be the same Date as the StartDate of the following phase. Here's the first six rows of the view as an example of how it should look:
Part Number Product Status Phase StartDate EndDate
FD 2000 Maintain Registration Initiation 2020-01-03 00:00:00.000 2020-01-08 00:00:00.000
FD 2000 Maintain Product Launch 2020-01-08 00:00:00.000 2020-01-15 00:00:00.000
FD 2000 Maintain Phase Out 2020-01-15 00:00:00.000 2020-01-17 00:00:00.000
FD 2000 Maintain Last Order 2020-01-17 00:00:00.000 2020-01-22 00:00:00.000
FD 2000 Maintain Last Shipment 2020-01-22 00:00:00.000 2020-01-31 00:00:00.000
FD 2000 Maintain Last Service 2020-01-31 00:00:00.000 2020-01-31 00:00:00.000
As you can see, the "Registration Initiation" EndDate is the same as the StartDate of "Product Launch". The last phase, "Last Service", will have the same StartDate and EndDate.
What's the best way to handle this? Should I create a calculated column before or after the transposition? This is just a snippet, there are thousands of rows of data just like this so I would like to optimize the code.
I think you want lead():
SELECT [Part Number], [Product Status], Phase, Date,
LEAD(Date) OVER (PARTITION BY [Part Number] ORDER BY Date) as Next_Date
FROM PLCMexample CROSS APPLY
(VALUES ('Phase1', [Phase1]),
('Phase2', [Phase2]),
('Phase3', [Phase3]),
('Phase4', [Phase4]),
('Phase5', [Phase5]),
('Phase6', [Phase6])
) CrossApplied (Phase, Date);
EDIT:
If you want a default value, use the 3-argument form of LEAD():
LEAD(Date, 1, DATEADD(DAY, 100, DATE) OVER (PARTITION BY [Part Number] ORDER BY Date) as Next_Date

case condition on join statement with two different join conditions in SQL RedShift

I have requirement that the cloumn value of name and id should return null in case of "b.end_dt < c.report_mnth"(less than) else "b.end_dt > c.report_mnth"(greater than) then column should show the value . I cant able to achieve this in a single query . I am trying this Amazon Redshift database . I have tried with some case condition but still I couldn't achieve this
As of now I have prepared a query that returns name and id as null in case of "b.end_dt < c.report_mnth"(less than)
Query which I have tried
SELECT null as name, null as id, b.strt_dt, b.end_dt from demo.v a
LEFT JOIN demo.emp_pos b on (a.pos_sk1=b.pos_sk and a.pos_id1=b.pos_id)
JOIN demo.load_status c on b.end_dt < c.report_mnth
Please help to solve this or at least need a clue to complete this.
Thanks in advance
Expected output
name id strt_dt end_dt
2015-04-12 2016-04-18
2015-04-12 2016-04-18
2015-04-12 2016-04-18
2015-04-12 2016-04-18
2015-04-12 2016-04-18
2015-04-12 2016-04-18
2016-02-02 2016-03-08
2016-02-02 2016-03-08
2016-02-02 2016-03-08
2016-02-02 2016-03-08
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
john ma abc12 2014-11-30 9999-12-31
Please let me know whether my answer is correct actually I have prepared two select query with the join condition like 1st query is to check less than i.e "b.end_dt < c.report_mnth" and return null value for 2 columns(name ,id) and 2nd query is to check the greater than i.e b.end_dt > c.report_mnth and return actual values for those 2 columns (name ,id) . I achieved this "UNION ALL"
Query
SELECT null as name,null as id, b.strt_dt,b.end_dt from demo.v a LEFT JOIN demo.emp_pos b on
( a.pos_sk1=b.pos_sk and a.pos_id1=b.pos_id) JOIN demo.load_status c on
b.end_dt < c.report_mnth
UNION ALL
SELECT null as name,null as id, b.strt_dt,b.end_dt from demo.v a LEFT JOIN demo.emp_pos b on
( a.pos_sk1=b.pos_sk and a.pos_id1=b.pos_id) JOIN demo.load_status c on
b.end_dt > c.report_mnth
I think I got my expected output.
Thanks guys

Oracle SQL query to get sales by date range

I am looking to write an SQL query that will provide me sales broken into date ranges, but it is a bit above my SQL knowledge.
I have a table of date ranges by customers as follows:
Cust Product startdate enddate
-----------------------------------
A 123 2011-01-01 2011-12-31
A 124 2011-01-01 2011-05-01
A 125 2011-01-01 2011-05-01
B 123 2011-01-01 2011-03-01
B 124 2011-01-01 2011-03-01
C 125 2011-02-02 2011-05-01
and sales stored as follows:
Cust Product date qty
-----------------------------------
A 123 2011-04-08 1
A 124 2011-01-01 12
A 125 2011-05-01 2
B 123 2011-01-04 3
B 124 2011-02-01 5
C 125 2011-03-01 80
The results should look something like:
Cust Product startdate enddate qty
-----------------------------------------
A 124 2011-01-01 2011-02-01 12
B 123 2011-01-01 2011-02-01 3
B 124 2011-02-02 2011-03-01 5
A 123 2011-03-02 2011-05-01 1
C 125 2011-03-02 2011-05-01 80
A 125 2011-05-02 2011-12-31 2
Any advice gratefully received.
I made the example in MySQL because Oracle server was down. But query is the same.
SQL Fiddle Demo
SELECT R.*, S.*
FROM dRanges R
JOIN Sales S
ON S.`date` >= R.`startdate`
AND S.`date` <= R.`enddate`
AND S.`Cust` = R.`Cust`
AND S.`Product` = R.`Product`
But you have to be carefull ranges doesnt overlap, otherwise you can have same Sales value appear on two ranges
EDIT Please explain the logic here

SQL Server : compare rows, exclude from results when some values are the same

I have the following SQL Server query problem.
If there is a row where Issue_DATE = as Maturity_Date in another row, and if both rows have the same ID and Amount USD, then none of these rows should be displayed.
Here is a simplified version of my table:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2010-01-01 00:00:00.000 2015-12-01 00:00:00.000 5000
1 2010-01-01 00:00:00.000 2001-09-19 00:00:00.000 700
2 2014-04-09 00:00:00.000 2019-04-09 00:00:00.000 400
1 2015-12-01 00:00:00.000 2016-12-31 00:00:00.000 5000
5 2015-02-24 00:00:00.000 2015-02-24 00:00:00.000 8000
4 2012-11-29 00:00:00.000 2015-11-29 00:00:00.000 10000
3 2015-01-21 00:00:00.000 2018-01-21 00:00:00.000 17500
2 2015-02-02 00:00:00.000 2015-12-05 00:00:00.000 12000
1 2015-01-12 00:00:00.000 2018-01-12 00:00:00.000 18000
2 2015-12-05 00:00:00.000 2016-01-10 00:00:00.000 12000
Result should be:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2010-01-01 00:00:00.000 2001-09-19 00:00:00.000 700
2 2014-04-09 00:00:00.000 2019-04-09 00:00:00.000 400
5 2015-02-24 00:00:00.000 2015-02-24 00:00:00.000 8000
4 2012-11-29 00:00:00.000 2015-11-29 00:00:00.000 10000
3 2015-01-21 00:00:00.000 2018-01-21 00:00:00.000 17500
1 2015-01-12 00:00:00.000 2018-01-12 00:00:00.000 18000
I tried with self join, but I do not get right result.
Thanks in advance!
Can you try something like this? 'not exists' is the way of doing it.
select * from table t1 where not exists (select 'x' from table t2 where t1.issue_date = t2.maturity_date and t1.amount_usd=t2.amount_usd and t1.id = t2.id)
I'd think about making subquery of all the dupes and then eliminating them from the first table like so:
select t1.ID
, t1.ISSUE_DATE
, t1.MATURITY_DATE
, t1.AMOUNT_USD
FROM
t1
LEFT JOIN
(select a.ID
, a.ISSUE_DATE
, a.MATURITY_DATE
, a.AMOUNT_USD
FROM
t1 a
INNER JOIN
ti b
) dupes
on
t1.ID = dupes.ID
WHERE dupes.ID IS NULL;

SQL Server query join several tables

I have a query that I don't think should be that hard to make, however, I've spent a lot of time on it now and still can't get it the way I want, so I hope someone here can help me.
Basically, I need to create a report that will give a value for each month, for each area. However, not all areas deliver data each month; in that case the view should return NULL for that month and area. So, the view need to look something like this:
Month Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3
2012-09-01 Area2 NULL
My data table looks something like this
Date Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3 -- Notice that Area2 is not present for September here
I have a table with all the available areas
Furthermore, I have created a table-valued function that returns all dates from a given date until now.
For example this statement
SELECT * FROM Periods_Months('2012-01-01')
would return 8 records like:
DateValue Year Month YearMonth
2012-01-01 00:00:00.000 2012 1 20121
2012-02-01 00:00:00.000 2012 2 20122
2012-03-01 00:00:00.000 2012 3 20123
2012-04-01 00:00:00.000 2012 4 20124
2012-05-01 00:00:00.000 2012 5 20125
2012-06-01 00:00:00.000 2012 6 20126
2012-07-01 00:00:00.000 2012 7 20127
2012-08-01 00:00:00.000 2012 8 20128
Based on the suggestions, my query now looks like this:
WITH months AS (
SELECT DateValue, YearMonth FROM Periods_Months('2011-01-01')
)
select m.DateValue
,CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,m.DateValue)+1,0)) AS Date) AS DateReported -- Get last day in month
,ResponseTime AS Value
,g.ExternalId
from GISDB.dbo.GisObjects g
CROSS JOIN months m
LEFT OUTER JOIN
( -- SELECT data from data table, grouped by area and month
SELECT dbo.YearMonth(CloseDate) AS YearMonth
,MAX(CloseDate) AS LastDate
,GisObjectId
,SUM(DATEDIFF(HH,RegDate,CloseDate)) AS ResponseTime -- calculate response time between start and end data (the value we need)
FROM DataTable
WHERE CloseDate IS NOT NULL
AND GisObjectId IS NOT NULL
GROUP BY GisObjectId, dbo.YearMonth(CloseDate) -- group by area and month
) c
ON g.ObjectId = c.GisObjectId AND c.YearMonth = m.YearMonth
WHERE g.CompanyId = 3 AND g.ObjectTypeId = 1 -- reduce the GIS objects that we compare to
ORDER BY m.DateValue, g.ObjectId
But the result is this (Value is always NULL):
DateValue DateReported Value ExternalId
2011-01-01 00:00:00.000 31-01-2011 NULL 9994
2011-01-01 00:00:00.000 31-01-2011 NULL 9993
2011-01-01 00:00:00.000 31-01-2011 NULL 9992
2011-01-01 00:00:00.000 31-01-2011 NULL 9991
2011-01-01 00:00:00.000 31-01-2011 NULL 2339
2011-01-01 00:00:00.000 31-01-2011 NULL 2338
2011-01-01 00:00:00.000 31-01-2011 NULL 2337
2011-01-01 00:00:00.000 31-01-2011 NULL 2336
2011-01-01 00:00:00.000 31-01-2011 NULL 2335
2011-01-01 00:00:00.000 31-01-2011 NULL 2334
2011-01-01 00:00:00.000 31-01-2011 NULL 2327
2011-01-01 00:00:00.000 31-01-2011 NULL 2326
2011-01-01 00:00:00.000 31-01-2011 NULL 2325
2011-01-01 00:00:00.000 31-01-2011 NULL 2324
2011-01-01 00:00:00.000 31-01-2011 NULL 2323
2011-01-01 00:00:00.000 31-01-2011 NULL 2322
etc.
I suppose you have a table with all your areas, which I call area_table.
WITH month_table AS (
SELECT dateValue FROM Periods_Months('2012-01-01')
)
select * from area_table
CROSS JOIN month_table
LEFT OUTER JOIN myValueTable
ON area_table.name = myValueTable.area
AND myValueTable.date = left(convert(varchar(30),month_table.dateValue,120),10)
ORDER BY myValueTable.Month, myValueTable.area
Suppose Areas is your table for all available areas, t - is your data table:
SELECT pm.dateValue,Ar.Area, t.value
FROM Periods_Months('2012-01-01') pm, Areas ar
left join t on (pm.dateValue=t.Date) and (ar.Area=t.Area)
order by pm.DateValue,ar.Area