How can I convert MS SQL T-SQL into Subsequent in MySQL - mysql

Here I have a MS SQL query which I would like to convert into MySQL. I don't know how to deal with that.
WITH PartitionData as (
SELECT
TOP 10 PurchaseDate.PurchaseDate AS date,
BatchCode,
ROW_NUMBER() OVER( PARTITION BY PurchaseDate.PurchaseDate ORDER BY ( SELECT NULL ) ) AS RowNumber
FROM tblNutBatches
INNER JOIN PurchaseDate ON PurchaseDate.PurchaseDate BETWEEN tblNutBatches.Introduction_date AND tblNutBatches.expiration_date
WHERE PurchaseDate.PurchaseDate = '2004-05-01'
)
SELECT
date as date,
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9]
FROM
(
SELECT
date,
BatchCode,
RowNumber
FROM
PartitionData
)AS DataAfterFilter
PIVOT
(
MAX(BatchCode) FOR RowNumber IN ([1], [2], [3], [4], [5], [6], [7], [8], [9])
) A
OUTPUT IN SQL SERVER
Any idea would be appreciated.

You would typically use conditional aggregation:
SELECT date,
MAX(CASE WHEN rn = 1 THEN BatchCode end) as BatchCode1,
MAX(CASE WHEN rn = 2 THEN BatchCode end) as BatchCode2,
...
MAX(CASE WHEN rn = 10 THEN BatchCode end) as BatchCode10
FROM (
SELECT
pd.PurchaseDate AS date,
BatchCode,
ROW_NUMBER() OVER(PARTITION BY pd.PurchaseDate ORDER BY BatchCode) AS rn
FROM tblNutBatches nb
INNER JOIN PurchaseDate pd ON pd.PurchaseDate BETWEEN nb.Introduction_date AND nb.expiration_date
WHERE pd.PurchaseDate = '2004-05-01'
) t
WHERE rn <= 10
GROUP BY date
Notes:
if you want a consistent results, you need ORDER BY clauses in the subquery and in ROW_NUMBER() - I used BatchCode
do prefix BatchCode with the alias of the table it belongs to
the PARTITION BY clause of ROW_NUMBER() and the outer GROUP BY clause are not necessary strictly speaking, since the subquery is filtering on just one data anyway; I retained them, in case you need to remove the filtering at some point. Accordingly, I moved the top 10 filtering logic form the subquery to the outer query.

Related

How to transpose values in rows to columns in MySQL

This image shows how my raw table looks like:
Following are the conditions to get the transposed table from the image below:
Each row has a unique id
We only need columns for groups A,B,C in the group field and not others.
There could be single or multiple id for group A for the same app id, I need to get those rows for which date is minimum.
There could be single or multiple id for group B and C for the same app id, I need to get those rows for which date is maximum
The image below shows how my final table should look like:
Each row has a unique id
We only need columns for groups A,B,C in the group field and not others.
add this to your query
WHERE `GROUP` IN ('A','B','C')
There could be single or multiple id for group A for the same app id, I need to get those rows for which date is minimum.
add somewhere after the SELECT:
MIN(date) OVER (PARTIITON BY appid)
There could be single or multiple id for group B and C for the same app id, I need to get those rows for which date is maximum
change the added option on point 3 to:
CASE WHEN `group` IN ('B','C')
THEN MAX(date) OVER (PARTIITON BY appid)
ELSE MIN(date) OVER (PARTIITON BY appid)
END
Maybe this helps you to try and take a serious request of solving this yourself (and learn from it) in stead of asking for a solution and then do copy/paste...
BTW: Naming fiels with reserved words, like GROUP and DATE is not a very smart thing to do. A better name for the column GROUP might be CategoryGroup (or whatever this group is referring to)
I took a different approach to this. The SQL is longer but I think it's more auditable.
The main logic point is that I broke A and BC into 2 different subqueries, and used QUALIFY ROW_NUMBER() to choose the correct row, based on either ASC or DESC per your requirements.
I know you are using mysql and this might not work since I don't have an instance to test this one, but here is the SQL I got from building this logic in Rasgo, which I tested on Snowflake and it worked.
-- This splits the data into group A only
WITH CTE_A AS (
SELECT
*
FROM
{{ your_table }}
WHERE
my_group = 'A'
),
-- This splits the data into group B and C only
CTE_B AS (
SELECT
*
FROM
{{ your_table }}
WHERE
my_group IN('B', 'C')
),
-- Selecting from A only, it keeps the most recent row ASCENDING
CTE_A_FIRST AS (
SELECT
*
FROM
CTE_A QUALIFY ROW_NUMBER() OVER (
PARTITION BY APP_ID,
MY_GROUP
ORDER BY
MY_DATE ASC
) = 1
),
-- Selecting from A only, it keeps the most recent row DESCENDING
CTE_B_LAST AS (
SELECT
*
FROM
CTE_B QUALIFY ROW_NUMBER() OVER (
PARTITION BY APP_ID,
MY_GROUP
ORDER BY
MY_DATE DESC
) = 1
),
-- Here we just union A and BC back to one another
CTE_ABC AS (
SELECT
ID,
APP_ID,
MY_DATE,
MY_GROUP,
SCORE1,
SCORE2
FROM
CTE_B_LAST
UNION ALL
SELECT
ID,
APP_ID,
MY_DATE,
MY_GROUP,
SCORE1,
SCORE2
FROM
CTE_B
),
-- We pivot the date horizontally so we get a date for A B C
-- the MIN does not matter, since at this point, we only have 1
CTE_PVT_DATE AS (
SELECT
APP_ID,
B,
C,
A
FROM
(
SELECT
APP_ID,
MY_DATE,
MY_GROUP
FROM
CTE_ABC
) PIVOT (
MIN (MY_DATE) FOR MY_GROUP IN ('B', 'C', 'A')
) as p (APP_ID, B, C, A)
),
-- We pivot the SCORE1 horizontally so we get a date for A B C
-- the MIN does not matter, since at this point, we only have 1
CTE_PVT_SCORE1 AS (
SELECT
APP_ID,
B,
C,
A
FROM
(
SELECT
APP_ID,
SCORE1,
MY_GROUP
FROM
CTE_ABC
) PIVOT (
MIN (SCORE1) FOR MY_GROUP IN ('B', 'C', 'A')
) as p (APP_ID, B, C, A)
),
-- We pivot the SCORE2 horizontally so we get a date for A B C
-- the MIN does not matter, since at this point, we only have 1
CTE_PVT_SCORE2 AS (
SELECT
APP_ID,
B,
C,
A
FROM
(
SELECT
APP_ID,
SCORE2,
MY_GROUP
FROM
CTE_ABC
) PIVOT (
MIN (SCORE2) FOR MY_GROUP IN ('B', 'C', 'A')
) as p (APP_ID, B, C, A)
),
-- We join the subqueries above together on the APP_IDs
CTE_JOINED AS (
SELECT
t0.*,
t1.APP_ID as SCORE1_APP_ID,
t1.B as SCORE1_B,
t1.C as SCORE1_C,
t1.A as SCORE1_A,
t2.APP_ID as SCORE2_APP_ID,
t2.B as SCORE2_B,
t2.C as SCORE2_C,
t2.A as SCORE2_A
FROM
CTE_PVT_DATE t0
INNER JOIN CTE_PVT_SCORE1 t1 ON t0.APP_ID = t1.APP_ID
INNER JOIN CTE_PVT_SCORE2 t2 ON t0.APP_ID = t2.APP_ID
)
-- The final select is really just renaming ...
-- the magic has already happened
SELECT
A AS DATE_A,
B AS DATE_B,
C AS DATE_C,
APP_ID,
SCORE1_B,
SCORE1_C,
SCORE1_A,
SCORE2_B,
SCORE2_C,
SCORE2_A
FROM
CTE_JOINED
I'll roll out my attempt along several steps and then show you the full solution made up of these steps, so that you can understand it piece by piece, given the following definition of your input table:
CREATE TABLE tab(
id INT,
app_id INT,
date VARCHAR(20),
group VARCHAR(20),
score1 INT,
score2 INT
);
STEP 1. Formatting date using a proper DATE format ("YYYY-MM-DD"). For this purpose the function STR_TO_DATE can come in handy.
WITH formatted_tab AS (
SELECT id,
app_id,
STR_TO_DATE(date, '%m/%d/%Y') AS date,
group,
score1,
score2
FROM tab
)
STEP 2. Extracting the useful dates according to the group field. As long as you treat group "A" differently with respect to group "B" and "C" specifically, the idea here is to address each group with a different query, where
in the former case the MIN aggregation function is applied,
in the latter case the MAX aggregation function is applied,
Then the two output result sets are combined with a UNION operation.
(
SELECT app_id,
MIN(date) AS date,
group
FROM formatted_tab
WHERE group IN ('A')
GROUP BY app_id,
group
UNION
SELECT app_id,
MAX(date) AS date,
group
FROM formatted_tab
WHERE group IN ('B', 'C')
GROUP BY app_id,
group
) needed_dates
STEP 3. Getting back scores corresponding to group and date field. This is done with a simple INNER JOIN between the last generated table and the formatted table.
(
SELECT needed_dates.*,
formatted_tab.score1,
formatted_tab.score2
FROM needed_dates
INNER JOIN formatted_tab
ON needed_dates.app_id = formatted_tab.app_id
AND needed_dates.date = formatted_tab.date
AND needed_dates.group = formatted_tab.group
) needed_infos
STEP 4. Pivoting the table exploiting MySQL tools like:
the IF statement to retrieve the values corresponding to a specific group
the MAX aggregation function, to aggregate on the same group
These tools are applied for each group you specified ('A', 'B' and 'C').
SELECT app_id,
MAX(IF(group='A', date , NULL)) AS date_groupA,
MAX(IF(group='B', date , NULL)) AS date_groupB,
MAX(IF(group='C', date , NULL)) AS date_groupC,
MAX(IF(group='A', score1, NULL)) AS score1_groupA,
MAX(IF(group='A', score2, NULL)) AS score2_groupA,
MAX(IF(group='B', score1, NULL)) AS score1_groupB,
MAX(IF(group='B', score2, NULL)) AS score2_groupB,
MAX(IF(group='C', score1, NULL)) AS score1_groupC,
MAX(IF(group='C', score2, NULL)) AS score2_groupC
FROM needed_infos
GROUP BY app_id
Full attempt. This is the combination of the previous snippets. The only difference is the presence of backticks for the field names, that avoid MySQL to misunderstand them with MySQL private keywords like "date" (indicating the DATE type), "group" (use as keyword in the GROUP BY clause) or similar.
WITH `formatted_tab` AS (
SELECT `id`,
`app_id`,
STR_TO_DATE(`date`, '%m/%d/%Y') AS `date`,
`group`,
`score1`,
`score2`
FROM `tab`
)
SELECT `app_id`,
MAX(IF(`group`='A', `date` , NULL)) AS date_groupA,
MAX(IF(`group`='B', `date` , NULL)) AS date_groupB,
MAX(IF(`group`='C', `date` , NULL)) AS date_groupC,
MAX(IF(`group`='A', `score1`, NULL)) AS score1_groupA,
MAX(IF(`group`='A', `score2`, NULL)) AS score2_groupA,
MAX(IF(`group`='B', `score1`, NULL)) AS score1_groupB,
MAX(IF(`group`='B', `score2`, NULL)) AS score2_groupB,
MAX(IF(`group`='C', `score1`, NULL)) AS score1_groupC,
MAX(IF(`group`='C', `score2`, NULL)) AS score2_groupC
FROM ( SELECT needed_dates.*,
formatted_tab.score1,
formatted_tab.score2
FROM ( SELECT `app_id`,
MIN(`date`) AS `date`,
`group`
FROM `formatted_tab`
WHERE `group` IN ('A')
GROUP BY `app_id`,
`group`
UNION
SELECT `app_id`,
MAX(`date`) AS `date`,
`group`
FROM `formatted_tab`
WHERE `group` IN ('B', 'C')
GROUP BY `app_id`,
`group`
) needed_dates
INNER JOIN formatted_tab
ON needed_dates.app_id = formatted_tab.app_id
AND needed_dates.date = formatted_tab.date
AND needed_dates.group = formatted_tab.group
) needed_infos
GROUP BY `app_id`
You'll find a tested SQL Fiddle here.

Ordered Analytical Functions not allowed in GROUP BY Clause

I have a query joining 2 tables with a SELECT statement for fields in both tables, and an Ordered Analytical Function to calculate the total volume for specific customers.
I'm getting the error "Ordered Analytical Functions not allowed in GROUP BY Clause" when I try to group the fields. I need the GROUP BY because there are other fields that need to be grouped but I also need the SUM() OVER (PARTITION BY()) for other calculations. How can I create the subquery so as to get rid of the error?
The query is something like this:
a.cust_no,
b.cust_name,
a.location,
c.product,
SUM(a.volume),
SUM(a.weight),
SUM(volume) OVER (PARTITION BY cust_no ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS fixed_volume,
SUM(CASE WHEN a_flag = 'Y' THEN volume ELSE 0 END) OVER (PARTITION BY cust_no ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS bias_volume
FROM test_table a
JOIN test_table2 b ON a.cust_no = b.cust_no
JOIN test_table3 c ON a.cust_no = c.cust_no
GROUP BY 1,2,3,4
There's no OLAP function in your GROUP BY clause.
I would expect a 3504 Selected non-aggregate values must be part of the associated group, this should fix it:
select
a.cust_no,
b.cust_name,
a.location,
c.product,
SUM(a.volume),
SUM(a.weight),
--volume is a detail row -> doesn't exist after aggregation
--SUM(a.volume) OVER (PARTITION BY cust_no) AS fixed_volume,
--aggregated volume can be used
SUM(SUM(a.volume)) OVER (PARTITION BY cust_no) AS fixed_volume,
--same logic
SUM(SUM(CASE WHEN a_flag = 'Y' THEN a.volume ELSE 0 END)) OVER (PARTITION BY cust_no ) AS bias_volume
FROM test_table a
JOIN test_table2 b ON a.cust_no = b.cust_no
JOIN test_table3 c ON a.cust_no = c.cust_no
GROUP BY 1,2,3,4

I need to get last created eligible rider ids and pinged rider ids accordeing to a orderId using a sql query

I need to get my data set as this table
I am trying to get eligible set like this, need to group_concat pinged set also
x.id IN (SELECT MAX(x.id) FROM x WHERE ping rider id IS NULL GROUP BY orderId)
You can assign a group based on the cumulative number of non-null values in eligible_riders. Then aggregate and take the last value:
select og.*
from (select order_id, grp, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders,
row_number() over (partition by order_id order by min(id) desc) as seqnum
from (select t.*,
sum(eligible_riders <> '') over (partition by order_id order by id) as grp
from t
) t
group by order_id, grp
) og
where seqnum = 1;
Hmmm . . . You could also do this with a correlated subquery, which might look a bit simpler:
select order_id, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders
from t
where t.id >= (select max(t2.id)
from t t2
where t2.order_id = t.order_id and
t2.eligible_riders <> ''
)
group by order_id;
For performance, you want an index on (order_id, eligible_riders).

Min in where clause in group using Row_Number() over order by

I have the below query which selects data from table COE3REL1. In the select statement it converts cyymmdd to yyyy-mm-dd. On the screen shot the table on the left is what is outputted current and table on the right is the desired outcome. I need to amend the where clause so it only selects the min id number in the group of Subscription E3BXDT. I am able to do this by outputting creating a table, and then writing a group by and select min (ID) from created table, however i need to action in 1 sql query.
Any support would be appreciated
Thanks
select
E3B8NB as "Subscription"
,date((((int(E3BXDT)/10000)+1900)|| '-' ||
(MOD(int(E3BXDT),10000) /100)|| '-' ||
MOD(int(E3BXDT),100))) "ST Effective Date"
,E3B6CD as "Tariff"
,ROW_NUMBER() over(order by E3B8NB) as ID
from DISEBTRD.COE3REL1
where E3BXDT > 1000000
order by E3B8NB,E3BXDT desc
You can try putting your query in a cte and then selecting where the row number = 1. You can try the following:
with maindata as (
select
E3B6CD as "Tariff"
, E3B8NB as "Subscription"
, date((((int(E3BXDT)/10000)+1900)|| '-' ||
(MOD(int(E3BXDT),10000) /100)|| '-' ||
MOD(int(E3BXDT),100))) "ST Effective Date"
, ROW_NUMBER() over(partition by E3B8NB order by E3BXDT asc) as ID
from DISEBTRD.COE3REL1
where E3BXDT > 1000000
)
select * from maindata
where id=1
order by Subscription,[ST Effective Date] desc
A row_number with a partition on the Subscription could help here.
And then wrap it in a sub-query, and filter in the outer-query.
SELECT
Subscription
, "ST Effective Date"
, Tariff
, ID
FROM
(
SELECT E3B8NB AS Subscription
, date(int(E3BXDT)+19000000) AS "ST Effective Date"
, E3B6CD AS Tariff
, ROW_NUMBER() OVER(ORDER BY E3B8NB, E3BXDT) AS ID
, ROW_NUMBER() OVER(PARTITION BY E3B8NB ORDER BY E3BXDT) AS RN
FROM DISEBTRD.COE3REL1
WHERE E3BXDT > 1000000
) q
WHERE RN = 1
ORDER BY ID

SQL get Max Date

I need to have the last price for each product for each client . I am not really good with SQL and I don't understand how I can do it.
Data :
What I want :
It is possible to have this data with a SQL request ?
Use window function ROW_NUMBER(), if available in your RDBMS:
SELECT product, price, date, client
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY product, client ORDER BY date DESC) rn
FROM mytable t
) x
WHERE rn = 1
In MySQL < 8.0:
SELECT product, price, date, client
FROM mytable t
WHERE NOT EXISTS (
SELECT 1
FROM mytable t1
WHERE t1.client = t.client AND t1.product = t.product AND t1.date > t.date
)
One option could be a correlated subquery
SELECT product, price, date, client
FROM tablename a where date =
(select max(date) from tablename b where a.product=b.product)