Combining data from multiple rows into one

Garth Turner :

First question on here, so i will try my best to be clear.

I have 2 tables:

  1. "TABLE1" which contains a record for each stock code and a list of attributes. In TABLE 1 there is just one record for each stock_code
  2. "TABLE2" which contains a log of changes to attributes of products, over time.

"TABLE2" contains the following fields:.

  1. stock_code
  2. stock_attribute
  3. old_value
  4. new_value
  5. change_date
  6. change_time

TABLE 2 has multiple entries ofr each stock_code.

Every time a stock item is change, another entry is made in Table2, with the attribute that has changed, the change date, time, old value and new value.

I want to create a query which will result in a table that has one record for each stock_code (from TABLE 1), and a column for each week over past year, with the value in each field being the last recorded "new_val" for that week (From TABLE 2)

I have tried

SELECT a.`stcode`, b.`week1`, b.`week2`, b.`week3`, b.`week4` etc. etc.
 from (SELECT stcode, )as a
 LEFT JOIN (SELECT stcode, 
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY)  AND DATE_SUB(CURDATE(),INTERVAL 357 DAY)  THEN newval END)week1,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 356 DAY)  AND DATE_SUB(CURDATE(),INTERVAL 350 DAY)  THEN newval END)week2,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 349 DAY)  AND DATE_SUB(CURDATE(),INTERVAL 343 DAY)  THEN newval END)week3,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 342 DAY)  AND DATE_SUB(CURDATE(),INTERVAL 336 DAY)  THEN newval END)week4,
(etc
etc
etc


    FROM (SELECT * from TABLE 2  ORDER BY "chngdate" DESC, "chngtime" DESC )as sub) as b ON b.stcode = s.stcode
ORDER BY stcode ASC

The trouble is with this, i get multiple lines for a stock_code which has mutliple entries.... for example, for stock_code abc123 the result i get is

STCODE       WEEK1     WEEK2     WEEK3      WEEK4      WEEK5     WEEK6
abc123        null      null       4         null      null       null
abc123        2         null       null      null      null       null
abc123        null      null       null      null      3          null

what i WANT is this:

STCODE       WEEK1     WEEK2     WEEK3      WEEK4      WEEK5     WEEK6
abc123        2         null       4         null      3        null

I have also tried the following, but teh query took so long, it never finished (there were 52 derived tables!)

SELECT a.`stcode`, w1.`new_value`, w2.`new_value`, w3.`new_value`, w4.`new_value` etc. etc.
 from (SELECT stcode, )as a
 LEFT JOIN (SELECT stcode, 
LEFT JOIN (SELECT stcode, depot, fieldname, chngdate, chngtime, newval from STDepotAmendmentsLog WHERE chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY)  AND DATE_SUB(CURDATE(),INTERVAL 357 DAY)  ORDER BY "chngdate" DESC, "chngtime" DESC)as w1 on s.stcode = w1.stcode
etc for week 2, 3, 4 etc etc
Salman A :

You could do the following:

  1. Find the greatest date for each "week"
  2. Find the rows corresponding to those dates
  3. Use conditional aggregation to convert rows into columns

Here is a rough outline of the code. It assumes that e.g. if today is 2020-03-03 then week 52 is from 2020-02-26 to 2020-03-03. Adjust if necessary:

SELECT t.stock_code
     , MAX(CASE WHEN weeknum = 51 THEN new_value END) AS week01
     , MAX(CASE WHEN weeknum = 50 THEN new_value END) AS week02
     , MAX(CASE WHEN weeknum =  1 THEN new_value END) AS week51
     , MAX(CASE WHEN weeknum =  0 THEN new_value END) AS week52
FROM table2 AS t
JOIN (
    SELECT stock_code
         , DATEDIFF(CURRENT_DATE, change_date) div 7 AS weeknum -- count multiples of 7
         , MAX(change_date) AS greatest_date
    GROUP BY stock_code, weeknum
    FROM table2
)  AS a ON t.stock_code = a.stock_code AND t.change_date = a.greatest_date
GROUP BY t.stock_code

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=22818&siteId=1