Select All Columns By Most Recent Date and Highest Version - sql-server-2008

I have been stumped on this for quite awhile. Request#, SlotId, Segment, and Version all make up the primary key. What i want from my stored proc is to be able to retrieve all rows by passing in the Request # and Segment, but for each slot i want the most recent effective date on or before todays date and from that i need the highest version #. I appriciate your time.
Values in database
Request# SlotId Segment Version Effective Date ContentId
A123 1 A 1 2012-01-01 1
A123 2 A 1 2012-01-01 2
A123 2 A 2 2012-02-01 34
A123 2 A 3 2012-02-01 24
A123 2 A 4 2015-01-01 6 //beyond todays date. dont want
Values I want to return from my stored proc when i pass in A123 for Request # and A for Segment.
A123 1 A 1 2012-01-01 1
A123 2 A 3 2012-02-01 24

The query could be written like this:
; WITH cte AS
( SELECT Request, SlotId, Segment, Version, [Effective Date], ContentId,
ROW_NUMBER() OVER ( PARTITION BY Request, Segment, SlotId
ORDER BY Version DESC ) AS RowN
FROM
tableX
WHERE
Request = #Req AND Segment = #Seg --- the 2 parameters
AND [Effective Date] < DATEADD(day, 1, GETDATE())
)
SELECT Request, SlotId, Segment, Version, [Effective Date], ContentId
FROM cte
WHERE Rn = 1 ;

Consider this:
;
WITH A as
(
SELECT DISTINCT
Request
, Segment
, SlotId
FROM Table1
)
SELECT A.Request
, A.SlotId
, A.Segment
, B.EffectiveDate
, B.Version
, B.ContentID
FROM A
JOIN (
SELECT Top 1
Request
, SlotId
, Segment
, EffectiveDate
, Version
, ContentId
FROM Table1 t1
WHERE t1.Request = A.Request
AND t1.SlotId = A.SlotId
AND T1.Segment = A.Segment
AND T1.EffectiveDate <= GetDate()
ORDER BY
T1.EffectiveDate DESC
, T1.Version DESC
) as B
ON A.Request = B.Request
AND A.SlotId = B.SlotId
AND A.Segment = B.Segment

Related

SQL How to select the value of the end of season(every three month)

Supposed I have some data as below:
code vol val num test_date
------------------------------------------
1 00001 500 0.1 111 20180105
2 00001 1000 0.2 222 20180304
3 00001 200 0.1 111 20180330
4 00001 400 0.3 222 20180601
5 00001 200 0.2 333 20180630
My expected result is
code vol val num test_date
------------------------------------------
1 00001 200 0.1 111 20180330
2 00001 200 0.2 333 20180630
3 00001 200 0.2 333 20180928 -- Max(val) only 0928, there is no data in 20180930
4 00001 200 0.2 333 20181231
I would like to select the max(val) for the month in '3, 6, 9 12', how to query in MySQL, thanks so much for any advice.
Since your dates are in numeric YYYYMMDD form, you can convert them to a "season" by integer dividing the date by 300. You can then find the maximum test_date per season and JOIN that back to the original table to get the values for that date:
SELECT d.*
FROM data d
JOIN (SELECT test_date DIV 300 AS quarter, MAX(test_date) AS max_date
FROM data
GROUP BY quarter) m ON m.max_date = d.test_date
Demo on dbfiddle
You can use quarter function available in mysql.
select * from test t1
inner join (
select quarter(test_date) qtr, max(val) val from test
group by quarter(test_date)) t2 on t2.val = t1.val and t2.qtr = quarter(t1.test_date)
see dbfiddle.
Hmmm . . . If you want the maximum value for each quarter, you can use window functions:
select t.*
from (select t.*,
row_number() over (partition by year(test_date), quarter(test_date) order by val desc) as seqnum
from t
) t
where seqnum = 1;
If you want the value on the last day of the quarter that is in the data, then use order by test_date desc instead:
select t.*
from (select t.*,
row_number() over (partition by year(test_date), quarter(test_date) order by test_date desc) as seqnum
from t
) t
where seqnum = 1;

MySQL last record in each group with multiple records in same date

Below is the sample data
row_id cust txn_dt txn_amount
-------------------------------------
1 1 31-01-2018 3000
2 1 04-02-2018 4000
3 1 04-02-2018 6000
4 2 29-01-2018 2500
5 2 02-02-2018 3900
6 1 01-02-2018 5000
7 1 01-02-2018 3900
Below is the Expected output
row_id cust txn_dt txn_amount
-------------------------------------
3 1 04-02-2018 6000
5 2 02-02-2018 3900
Need to pick the latest record for each customer based on date and then row_id
It is tricky when there are two columns that define the ordering. Here is one method:
select t.*
from t
where t.row_id = (select t2.row_id
from t t2
where t2.cust = t.cust
order by t2.txn_date desc, row_id desc
limit 1
);
An index on t(cust, txn_date, row_id) should help performance a bit.
Here's an approach that will return the specified result:
SELECT t.row_id
, t.cust
, t.txn_date
, t.txn_amount
FROM ( SELECT r.cust
, MAX(r.row_id) AS max_row_id
FROM ( SELECT p.cust
, DATE_FORMAT(
MAX(
STR_TO_DATE( p.txn_date ,'%d-%m-%Y')
)
,'%d-%m-%Y'
) AS max_txn_date
FROM sample_data p
GROUP BY p.cust
) q
JOIN sample_data r
ON r.cust = q.cust
AND r.txn_date = q.max_txn_date
GROUP BY r.cust
) s
JOIN sample_data t
ON t.cust = s.cust
AND t.row_id = s.max_row_id
ORDER BY t.row_id ASC
Inline view q gets the latest txn_date for each cust
inline view s gets the maximum row_id value for the latest txn_date for each cust
(If txn_date column was DATE datatype, we could avoid the rigmarole of the STR_TO_DATE and DATE_FORMAT functions. And with an appropriate index available, we would (likely) avoid a full scan and an expensive "Using filesort" operation.)

Unable to date range query results in MySQL

I am having trouble querying the correct date range. My query does not seem to consider the Where clause for post_date > (date provided).
SELECT `code`,
`description`,
SUM( IF( month = 3 && year = 2018, monthly_quantity_total, 0 ) ) AS monthlyqt,
SUM( IF( month = 3 && year = 2018, monthly_price_total, 0 ) ) AS monthlypt,
SUM( monthly_quantity_total ) AS yearlyqt,
SUM( monthly_price_total ) AS yearlypt
FROM (
SELECT `invoices_items`.`code`,
`invoices_items`.`description`,
SUM( invoices_items.discounted_price * invoices_items.quantity_supplied ) AS monthly_price_total,
SUM( invoices_items.quantity_supplied ) AS monthly_quantity_total,
YEAR( invoices_items.datetime_created ) AS year,
MONTH( invoices_items.datetime_created ) AS month
FROM `invoices_items`
JOIN `invoices` ON `invoices`.`id` = `invoices_items`.`invoice_id`
WHERE `invoices`.`is_finalised` = 1
AND `invoices`.`post_date` > 2018-02-28
AND `invoices_items`.`type` = 1
GROUP BY `year`, `month`, `invoices_items`.`code`
UNION ALL
SELECT `credit_notes_items`.`code`,
`credit_notes_items`.`description`,
SUM( credit_notes_items.discounted_price * credit_notes_items.quantity_supplied * -1 ) AS monthly_price_total,
SUM( credit_notes_items.quantity_supplied ) AS monthly_quantity_total,
YEAR( credit_notes_items.datetime_created ) AS year,
MONTH( credit_notes_items.datetime_created ) AS month
FROM `credit_notes_items`
JOIN `credit_notes` ON `credit_notes`.`id` = `credit_notes_items`.`credit_note_id`
WHERE `credit_notes`.`is_finalised` = 1
AND `credit_notes`.`post_date` > 2018-02-28
AND `credit_notes_items`.`type` = 1
GROUP BY `year`, `month`, `credit_notes_items`.`code`
) AS sub
GROUP BY code;
There are basically 4 tables being queried here. invoices, invoices_items, credit_notes and credit_notes_items.
table 1 - invoices
id post_date is_finalised
1 2018-01-01 1
2 2018-02-01 1
3 2018-03-01 1
table 2 - invoices_items
id invoice_id code description discounted_price quantity_total type
1 1 TEST-01 Test product 9.99 1 1
2 1 TEST-01 Test product 9.99 2 1
3 2 TEST-01 Test product 9.99 5 1
4 3 TEST-01 Test product 9.99 5 1
I have give some example rows above. From the table 1 and 2 above the desired result should be;
Desired Output
code description monthyqt monthlypt yearlyqt yearlypt
TEST-01 Test product 5 49.95 5 49.95
However the output I am receiving is as below;
Received Output
code description monthyqt monthlypt yearlyqt yearlypt
TEST-01 Test product 5 49.95 13 129.87
The query works as intended except for the date range I am trying to achieve by using the Where clause. You can see I am trying to filter out any row which are not matching invoices.post_date > 2018-02-28 (and also credit_notes.post_date > 2018-02-28).
I am not sure what I have done wrong here but any help will be much appreciated.
Thanks in advance.
I worked it out. Basically the variable which was parsing the date value needed to be encapsulated in single quotes.
e.g.
invoices.post_date > '2018-02-28'
It's always something this simple which throws you off.

MySql-Pivot Table

I have a database table (return_period) having records
id ReturnPeriod Value Date
1 10 10X 11/1/2012
2 20 20x 11/1/2012
3 30 30x 11/1/2012
4 10 10xx 12/1/2013
5 20 20xx 12/1/2013
6 30 30y 1/1/2015
7 30 303 1/1/2015
and expecting an output table like below:
Date Rp10_Value Rp20_Value Rp30_Value
11/1/2012 10x 20x 30x
12/1/2013 10XX 20XX
1/1/2015 30y
1/1/2015 303
I want records based on the dates(want the multiple records).Is there a way I can write a query to this type of requirement.Thanks
select date,
case when rp=10 then value else null end as Rp10_Value,
case when rp=10 then value else null end as Rp20_Value,
case when rp=10 then value else null end as Rp30_Value
from
(
SELECT date, value, ReturnPeriod
FROM Table2 where ReturnPeriod=10
union all
SELECT date, value, ReturnPeriod
FROM Table2 where ReturnPeriod=20
union all
SELECT date, value, ReturnPeriod
FROM Table2 where ReturnPeriod=30
) ;
This is a pivot. In MySQL, you can use conditional aggregation:
select rp.date,
max(case when returnperiod = 10 then value end) as rp10_value,
. . .
from return_period rp
group by rp.date;
EDIT:
I see you have duplicates. The same idea applies, but you need to include a sequence number:
select rp.date,
max(case when returnperiod = 10 then value end) as rp10_value,
. . .
from (select rp.*,
row_number() over (partition by date, returnperiod order by date) as seqnum
from return_period rp
) rp
group by rp.date, seqnum;

Help with a nested query in MySQL

I am trying to do some calculations based on record from master table and wanted store manipulated result into a separate test table.
>Table:Master:
>C1 C2 C3 C4
>---------- -------- -- --
>2011-02-19 Test-A 31 3
>2011-02-19 Test-B 34 3
>2011-02-19 Test-C 17 1
>2011-02-15 Test-A* 48 =I 4
>2011-02-15 Test-B 64 6
>2011-02-15 Test-C 55 5
>2011-02-11 Test-A 64 =I2 6
>2011-02-11 Test-B 53 5
>2011-02-11 Test-C 17 1
>2011-02-10 Test-A 12 =I3 1 =J
>2011-02-10 Test-B 02 0
>2011-02-10 Test-C 54 5
Three kinds of test conducted in random fashions in a same day; but for this case date is not much important; only last three test records are used for the calculation.
I am trying to perform sequential calculations as below; using 3rd oldest element. for example, for test A, I(iteration) will be 48 (3rd oldest record = column c3) and therefore R2 & R3 will be calculated based on I2 & I3. And at last displaying average of, R,R2,R3 - J. ( C4 = latest record. )
Expected result:
>Table:Test-A
>SR Date I I2 I3 I4
>-- ---------- ----- ----------- ----------- -------------------
>1 2011/02/17 48 -52.96 -24.18 -10.71
>Formula:
>SR Date R R2 R3 R4
>-- ---------- ----- ----------- ----------- -------------------
>1 today() 48=C3 (I*0.23-I2) (I*0.23-I3) =avg(I,I1,I2,I3)-C4
I guess I need to use sub/nested query with join, but i couldn't able to figure out how to handle I; all result will be placed in individual test tables. Your input will be much appreciated. TIA
Setup test case:
CREATE TABLE `m1`
(c1 DATE
,c2 VARCHAR(6)
,c3 SMALLINT
,c4 TINYINT
) DEFAULT CHARSET=latin1;
INSERT INTO `m1` VALUES
('2011-02-19','Test-A',31,3)
,('2011-02-19','Test-B',34,3)
,('2011-02-19','Test-C',17,1)
,('2011-02-15','Test-A',48,4)
,('2011-02-15','Test-B',64,6)
,('2011-02-15','Test-C',55,5)
,('2011-02-11','Test-A',64,6)
,('2011-02-11','Test-B',53,5)
,('2011-02-11','Test-C',17,1)
,('2011-02-10','Test-A',12,1)
,('2011-02-10','Test-B',02,0)
,('2011-02-10','Test-C',54,5);
This query makes use of one local variable (#i). Provide the test_name ('Test-A') and the date ('2011-02-17') in the query, shown as literals here.
SELECT o.tn AS `Test`
, o.dt AS `Date`
, SUM(CASE WHEN o.n = 1 THEN o.c3*1.00 ELSE NULL END) AS R
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 2 THEN -1.00*o.c3 ELSE NULL END) AS R2
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 3 THEN -1.00*o.c3 ELSE NULL END) AS R3
, AVG(CASE WHEN o.n < 4 THEN c3*1.00 ELSE NULL END)-SUM(CASE WHEN n = 3 THEN c4*1.00 ELSE NULL END) AS R4
FROM (
SELECT #i := #i + 1 AS n
, s.tn
, s.dt
-- , m.c1
, m.c3
, m.c4
FROM (SELECT '2011-02-17' AS dt,_latin1'Test-A' AS tn, #i := 0) s
JOIN m1 m
ON m.c2 = s.tn AND m.c1 <= s.dt
ORDER BY m.c1 DESC
LIMIT 0,3
) o
GROUP BY o.tn, o.dt
HAVING SUM(1) >= 3
You can run just the inner query, uncomment the m.c1 from the select list, to check the rows returned (1st, 2nd and 3rd latest, prior to the supplied date.
This query returns a different value for R3 than shown in the question, but the result returned by the query appears to be the correct result for the given formula.
Also, the formula for R4 references 5 values: avg(I,I1,I2,I3)-J3. The formula used in the query is effectively =avg(I1,I2,I3)-J3
To get the result for all tests, as of a given date:
SELECT o.tn AS `Test`
, o.dt AS `Date`
, SUM(CASE WHEN o.n = 1 THEN o.c3 ELSE NULL END) AS R
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 2 THEN -1.00*o.c3 ELSE NULL END) AS R2
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 3 THEN -1.00*o.c3 ELSE NULL END) AS R3
, AVG(CASE WHEN o.n <= 3 THEN c3*1.00 ELSE NULL END)-SUM(CASE WHEN n = 3 THEN c4 ELSE NULL END) AS R4
FROM (
SELECT #i := CASE WHEN #prev_tn = m.c2 THEN #i + 1 ELSE 1 END AS n
, #prev_dt := s.dt AS dt
, #prev_tn := m.c2 AS tn
, m.c1
, m.c3
, m.c4
FROM (SELECT '2011-02-17' AS dt, #i := 0, #prev_tn := NULL) s
JOIN m1 m
ON m.c1 <= s.dt
ORDER BY s.dt, m.c2, m.c1 DESC
) o
GROUP BY o.tn, o.dt
HAVING SUM(1) >= 3
(The HAVING clause guarantees that the query returns results only if there are at least three rows for a given test, preceding the given date.) Here is the query output for two different dates, the 17th and the 20th:
Test Date R R2 R3 R4
------ ---------- -- ------ ------ -----
Test-A 2011-02-17 48 -52.96 -0.96 40.33
Test-B 2011-02-17 64 -38.28 12.72 39.67
Test-C 2011-02-17 55 -4.35 -41.35 37.00
Test Date R R2 R3 R4
------ ---------- -- ------ ------ -----
Test-A 2011-02-20 31 -40.87 -56.87 41.67
Test-B 2011-02-20 34 -56.18 -45.18 45.33
Test-C 2011-02-20 17 -51.09 -13.09 28.67
(The query would be somewhat more involved, to get results for more than one date.)
This may not be the best way to solve the problem, but I've successfully used this approach with MySQL.