I have the following query
SELECT orderno,
CASE WHEN param_id = 'variant' THEN param_val END AS 'variant',
CASE WHEN param_id = 'period_from' THEN param_val END AS'period_from',
CASE WHEN param_id = 'period_to' THEN param_val END AS'period_to',
CASE WHEN param_id = 'division' THEN param_val END AS 'division',
CASE WHEN param_id = 'show_div' THEN param_val END AS 'show_div',
CASE WHEN param_id = 'group_div' THEN param_val END AS 'group_div',
FROM orderreport
order by orderno
This returns a grid similar to the below (there are another number of columns which I have removed for the purpose of the question) There is also an infinite number of order nos
orderno | variant | period_from | period_to | division | show_div | group_div
3 | AH003 | NULL | NULL | NULL | NULL | NULL
3 | NULL | 201300 | NULL | NULL | NULL | NULL
3 | NULL | NULL | 201304 | NULL | NULL | NULL
3 | NULL | NULL | NULL | SALES | NULL | NULL
3 | NULL | NULL | NULL | NULL | Y | NULL
3 | NULL | NULL | NULL | NULL | NULL | Y
My desired output is as the below, no matter what i have tried I am stumped.
orderno | variant | period_from | period_to | division | show_div | group_div
3| AH003 | 201300 | 201304 | SALES | Y | Y
Your query is very close, I would add an aggregate function around your CASE expressions and then a GROUP BY:
SELECT orderno,
max(CASE WHEN param_id = 'variant' THEN param_val END) AS variant,
max(CASE WHEN param_id = 'period_from' THEN param_val END) AS period_from,
max(CASE WHEN param_id = 'period_to' THEN param_val END) AS period_to,
max(CASE WHEN param_id = 'division' THEN param_val END) AS division,
max(CASE WHEN param_id = 'show_div' THEN param_val END) AS show_div,
max(CASE WHEN param_id = 'group_div' THEN param_val END) AS group_div
FROM orderreport
group by orderno
order by orderno
Related
I am running an Oracle database and have two tables below.
#account
+----------------------------------+
| acc_id | date | acc_type |
+--------+------------+------------+
| 1 | 11-07-2018 | customer |
| 2 | 01-11-2018 | customer |
| 3 | 02-09-2018 | employee |
| 4 | 01-09-2018 | customer |
+--------+------------+------------+
#credit_request
+-----------------------------------------------------------------+
| credit_id | date | credit_type | acc_id | credit_amount |
+------------+-------------+---------- +--------+
| 1112 | 01-08-2018 | failed | 1 | 2200 |
| 1214 | 02-12-2018 | success | 2 | 1500 |
| 1312 | 03-11-2018 | success | 4 | 8750 |
| 1468 | 01-12-2018 | failed | 2 | 3500 |
+------------+-------------+-------------+--------+---------------+
Want to have followings for each customer:
the last successful credit_request
sum of credit_amount of all failed credit_requests
Here is one method:
select a.acct_id, acr.num_fails,
acr.num_successes / nullif(acr.num_fails) as ratio, -- seems weird. Why not just the failure rate?
last_cr.credit_id, last_cr.date, last_cr.credit_amount
from account a left join
(select acc_id,
sum(case when credit_type = 'failed' then 1 else 0 end) as num_fails,
sum(case when credit_type = 'failed' then credit_amount else 0 end) as num_fails,
sum(case when credit_type = 'success' then 1 else 0 end) as num_successes
max(case when credit_type = 'success' then date else 0 end) as max_success_date
from credit_request
group by acct_id
) acr left join
credit_request last_cr
on last_cr.acct_id = acr.acct_id and last_cr.date = acr.date;
The following query should do the trick.
SELECT
acc_id,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN credit_id END) as last_successfull_credit_id,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN cdate END) as last_successfull_credit_date,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN credit_amount END) as last_successfull_credit_amount,
SUM(CASE WHEN credit_type = 'failed' THEN credit_amount ELSE 0 END) total_amount_of_failed_credit,
SUM(CASE WHEN credit_type = 'failed' THEN 1 ELSE 0 END) / COUNT(*) ratio_success_request
FROM (
SELECT
a.acc_id,
a.cdate adate,
a.acc_type,
c.credit_id,
c.cdate,
c.credit_type,
c.credit_amount,
ROW_NUMBER() OVER(PARTITION BY c.acc_id, c.credit_type ORDER BY c.cdate DESC) rn
FROM
account a
LEFT JOIN credit_request c ON c.acc_id = a.acc_id
) x
GROUP BY acc_id
ORDER BY acc_id
The subquery assigns a sequence to each record, within groups of accounts and credit types, using ROW_NUMBR(). The outer query does conditional aggrgation to compute the different computation you asked for.
This Db Fiddle demo with your test data returns :
ACC_ID | LAST_SUCCESSFULL_CREDIT_ID | LAST_SUCCESSFULL_CREDIT_DATE | LAST_SUCCESSFULL_CREDIT_AMOUNT | TOTAL_AMOUNT_OF_FAILED_CREDIT | RATIO_SUCCESS_REQUEST
-----: | -------------------------: | :--------------------------- | -----------------------------: | ----------------------------: | --------------------:
1 | null | null | null | 2200 | 1
2 | 1214 | 02-DEC-18 | 1500 | 3500 | .5
3 | null | null | null | 0 | 0
4 | 1312 | 03-NOV-18 | 8750 | 0 | 0
This might be what you are looking for... Since you did not show expected results, this might not be 100% accurate, feel free to adapt this.
I guess the below query is easy to understand and implement. Also, to avoid more and more terms in the CASE statements you can just make use of WITH clause and use it in the CASE statements to reduce the query size.
SELECT a.acc_id,
c.credit_type,
(distinct c.credit_id),
CASE WHEN
c.credit_type='success'
THEN max(date)
END CASE,
CASE WHEN
c.credit_type='failure'
THEN sum(credit_amount)
END CASE,
(CASE WHEN
c.credit_type='success'
THEN count(*)
END CASE )/
( CASE WHEN
c.credit_type='failure'
THEN count(*)
END CASE)
from accounts a LEFT JOIN
credit_request c on
a.acc_id=c.acc_id
where a.acc_type= 'customer'
group by c.credit_type
I have a table that contains a lot of data, but the relevant data in the table looks something like this:
Orders table:
+----------+-----------+---------------+
| OrderID | Product | Date |
+----------+-----------+---------------+
| 1 | Apple | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Orange | 01/01/2001 |
| 1 | Pineapple | 01/01/2001 |
| 2 | Cherry | 02/02/2002 |
| 2 | Cherry | 02/02/2002 |
| 3 | Orange | 03/03/2003 |
| 3 | Apple | 03/03/2003 |
| 3 | Cherry | 03/03/2003 |
+----------+-----------+---------------+
I'd like a query to return a distinct list of orders, and if the order contains certain products, to indicate as such:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
Here's where I've left off and decided to seek out help:
WITH CTEOrder AS
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS OrderRN
FROM
Orders
)
CTEApple as
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS AppleRN
FROM
Orders
WHERE
Product = 'Apple'
),
CTEPear
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS PearRN
FROM
Orders
WHERE
Product = 'Pear'
)
SELECT
o.OrderID, o.Product, o.Date,
co.OrderRN, a.AppleRN, p.PearRN
FROM
Orders AS o
OUTER JOIN
CTEOrder AS co ON o.OrderID = co.Orderid
OUTER JOIN
CTEApple AS a ON o.OrderID = a.OrderID
OUTER JOIN
CTEPear AS p ON o.OrderID = p.OrderID
WHERE
(co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN = 1)
Currently my result set is unwieldy with a significant amount of duplication.
I'm thinking that I am heading in the wrong direction, but I don't know what other tools are available to me within SQL Server to cut up this data the way I need.
Thanks for any guidance!
Here's my result set after Nik Shenoy's guidance:
+----------+-----------+----------------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+----------------+
| 1 | 01/01/2001| x | NULL |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | NULL |
| 1 | 01/01/2001| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 3 | 03/03/2003| NULL | NULL |
| 3 | 03/03/2003| x | NULL |
| 3 | 03/03/2003| NULL | NULL |
+----------+-----------+----------------+
What is my next step to have only 1 row per Order:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
You can just use conditional aggregation:
select o.orderid, date,
max(case when product = 'Apple' then 'X' end) as IsApple,
max(case when product = 'Pear' then 'X' end) as IsPear
from orders o
group by o.orderid, date;
If you know all the products in advance, you can use the Transact-SQL PIVOT relational operator to cross-tabulate the data by product. If you use MAX or COUNT, you can just transform non-NULL or non-ZERO output to an 'x'
SELECT
PivotData.OrderID
, PivotData.OrderDate
, CASE WHEN PivotData.Apple IS NULL THEN '' ELSE 'X' END AS [Apple?]
, CASE WHEN PivotData.Pear IS NULL THEN '' ELSE 'X' END AS [Pear?]
, CASE WHEN PivotData.Orange IS NULL THEN '' ELSE 'X' END AS [Orange?]
, CASE WHEN PivotData.Pineapple IS NULL THEN '' ELSE 'X' END AS [Pineapple?]
, CASE WHEN PivotData.Cherry IS NULL THEN '' ELSE 'X' END AS [Cherry?]
FROM
(SELECT OrderID, Product, OrderDate) AS [Order]
PIVOT (MAX(Product) FOR Product IN ( [Apple], [Pear], [Orange], [Pineapple], [Cherry] )) AS PivotData
I have three tables that are structured in this format:
DECLARE #A
(
TypeName varchar(100),
Type1 varchar(3),
Type2 varchar(3),
Type3 varchar(3),
Type4 varchar(3),
Type5 varchar(3),
Type6 varchar(3)
)
Data:
| Bob | null | null | null | null | null | null |
| Steve | null | null | null | null | null | null |
| Bill | null | null | null | null | null | null |
...
DECLARE #B
(
NameID int,
Name varchar(100)
)
Data:
| 1 | Bob |
| 2 | Steve |
| 3 | Bill |
...
DECLARE #C
(
NameID int,
Type int
)
Data:
| 1 | 1 |
| 1 | 3 |
| 2 | 1 |
| 3 | 2 |
...
I want to update the Type# columns in table #A with a condition based on the Type column of table #C. Here is an SQL query that I have attempted to use:
UPDATE #A SET
Type1 = (CASE WHEN c.Type = 6 THEN 'Yes' ELSE 'No' END),
Type2 = (CASE WHEN c.Type = 1 THEN 'Yes' ELSE 'No' END),
Type3 = (CASE WHEN c.Type = 2 THEN 'Yes' ELSE 'No' END),
Type4 = (CASE WHEN c.Type = 3 THEN 'Yes' ELSE 'No' END),
Type5 = (CASE WHEN c.Type = 4 THEN 'Yes' ELSE 'No' END),
Type6 = (CASE WHEN c.Type = 5 THEN 'Yes' ELSE 'No' END),
FROM #A
INNER JOIN #B b ON b.Name = TypeName
INNER JOIN #C c ON c.NameID = b.NameID
SELECT * FROM #A
Instead of getting the expected values:
| Bob | No | Yes | No | Yes | No | No | <-- Correct
| Steve | No | Yes | No | No | No | No |
| Bill | No | No | Yes | No | No | No |
I get these values:
| Bob | No | Yes | No | No | No | No | <-- Incorrect (noticed the third column from the right being set to No instead of Yes)
| Steve | No | Yes | No | No | No | No |
| Bill | No | No | Yes | No | No | No |
I have also tried re-writing the statement to be like this:
UPDATE #A SET
Type1 = (CASE WHEN t.Type = 6 THEN 'Yes' ELSE 'No' END),
Type2 = (CASE WHEN t.Type = 1 THEN 'Yes' ELSE 'No' END),
Type3 = (CASE WHEN t.Type = 2 THEN 'Yes' ELSE 'No' END),
Type4 = (CASE WHEN t.Type = 3 THEN 'Yes' ELSE 'No' END),
Type5 = (CASE WHEN t.Type = 4 THEN 'Yes' ELSE 'No' END),
Type6 = (CASE WHEN t.Type = 5 THEN 'Yes' ELSE 'No' END),
FROM
(
SELECT b.Name, c.Type
FROM #C c
INNER JOIN #B b on b.NameID = c.NameID
) t
WHERE t.Name = TypeName
But it still returned the latter results.
So why is it that the columns are not being updated correctly for the TypeName column Bob and how would I fix that so that I can get my expected values?
The problem is that you have several rows in #C per person. You'll have to first make it one row to be able to update the data:
select
NameID,
max(case Type when 1 then 1 else 0 end) as Type1,
max(case Type when 2 then 1 else 0 end) as Type2,
max(case Type when 3 then 1 else 0 end) as Type3,
max(case Type when 4 then 1 else 0 end) as Type4,
max(case Type when 5 then 1 else 0 end) as Type5,
max(case Type when 6 then 1 else 0 end) as Type6
from
#C
group by
NameID
Can't test this now, but you probably can even make the text there, with something like this:
case when max(case Type when 1 the 1 else 0) = 1 then 'Yes' else 'No'
end as Type1
You can use this as derived table in the update, or maybe collect the results first into another table variable
I'm looking for a way of pulling totals from a transactions table. Sales and return transactions are differentiated by a column, but the value is always stored as a positive.
I've managed to pull the different transaction type totals, grouped by product, as separate rows:
SELECT `type`,
`product`,
sum(`final_price`) AS `value`,
count(`final_price`) AS `count`
GROUP BY `product`, `type`
The result is:
Type | Product | Value | Count
S | 1 | 1000 | 2
S | 4 | 750 | 3
S | 2 | 300 | 2
S | 3 | 10 | 1
R | 1 | 500 | 1
Ideally, I'd like to have the totals displayed on a single row but in additional columns different columns for ordering purposes. The ideal result would be:
Type | Product | s_value | s_count | r_value | r_count
S | 1 | 1000 | 2 | 500 | 1
S | 4 | 750 | 3 | 0 | 0
S | 2 | 300 | 2 | 0 | 0
S | 3 | 10 | 1 | 0 | 0
I've tried union all and left joins with no joys so far.
You can use case expressions to differentiate by the type of transaction:
SELECT `product`,
SUM(CASE `type` WHEN 'S' THEN `final_price` END) AS `s_value`,
COUNT(CASE `type` WHEN 'S' THEN `final_price` END) AS `s_count`,
SUM(CASE `type` WHEN 'R' THEN `final_price` END) AS `r_value`,
COUNT(CASE `type` WHEN 'R' THEN `final_price` END) AS `r_count`
GROUP BY `product`, `type`
EDIT:
By the forward-quotes around the column names I'm assuming this is a MySQL questions even though it's not explicitly tagged as such.
If this is the case, you can simplify the count statements by utilizing MySQL's automatic conversion from Boolean to int which takes true as a 1 and false as a 0:
SELECT `product`,
SUM(CASE `type` WHEN 'S' THEN `final_price` END) AS `s_value`,
SUM(`type` = 'S') AS `s_count`,
SUM(CASE `type` WHEN 'R' THEN `final_price` END) AS `r_value`,
SUM(`type` = 'R') AS `r_count`
GROUP BY `product`, `type`
I am trying to write a validation for the following set of data:
SSYS | Material_Number | Characteristic | Description
001 | 000000000001111 | SH_DESC | TEST
001 | 000000000001111 | DESIGN_TYPE | NULL
001 | 000000000001111 | VOLTAGE | NULL
001 | 000000000009999 | SH_DESC | TEST2
001 | 000000000009999 | OPER_METHOD | LIGHT
001 | 000000000009999 | FILTER_TYPE | Filter element,Air
001 | 000000000014560 | SH_DESC | Horn,Signal
001 | 000000000014560 | DIMENSION_SIZE | NULL
001 | 000000000014560 | FILTER_TYPE | NULL
I would like to group by the Material_Number and count as 1 (ie. true) if within the Material_Number group, the SH_DESC description is NOT NULL and all other characteristics' descriptions IS NULL. So, in this case my result would be:
SSYS | Material_Number | Characteristic | Description | COUNT
001 | 000000000001111 | SH_DESC | TEST | 1
001 | 000000000009999 | SH_DESC | TEST2 | 0
001 | 000000000014560 | SH_DESC | Horn,Signal | 1
My attempt:
Select COUNT (*), SSYS, Material_Number, Characteristic, Description
From myDB where (Characteristic = 'SH_DESC' AND DESCRIPTION IS NOT NULL) AND (Characteristic NOT IN ('SH_DESC') IS NULL)
GROUP BY SSYS, Material_Number, Characteristic, Description HAVING COUNT (*) < 2
Any help is much appreciated!
Try:
Select SSYS,
Material_Number,
'SH_DESC' Characteristic,
MAX(CASE WHEN Characteristic = 'SH_DESC' THEN Description END) Description,
CASE WHEN MAX(CASE WHEN Characteristic = 'SH_DESC' THEN Description END) IS NOT NULL AND
MAX(CASE WHEN Characteristic <>'SH_DESC' THEN Description END) IS NULL
THEN 1
ELSE 0
END COUNT
From myDB
GROUP BY SSYS, Material_Number
Try this:
select ssys, material_number, 'SH_DESC' as characteristic,
(case when sum(case when characteristic is not null and characteristic<> 'SH_DESC' and description is null then 1 else 0 end) = count(*) - 1
then 1
else 0
end) as count
from t
group by ssys, material_number
It groups by material and counts the number of rows that have non-null characterist where the description is null. It sets count accordingly.
An alternative to the GROUP BY and SUM(CASE WHEN) options...
SELECT
*,
CASE WHEN Description IS NULL THEN 0
WHEN EXISTS (SELECT *
FROM myDB as lookup
WHERE lookup.SSYS = myDB.SSYS
AND lookup.Material_Number = myDB.Material_Number
AND lookup.Characteristic <> 'SH_DESC'
AND lookup.Description IS NOT NULL) THEN 0
ELSE 1 END as myCount
FROM
myDB
WHERE
Characteristic = 'SH_DESC'
Try this -- Here I guess you cant get the description bcos there is nothiing to filter the specific description.
CREATE TABLE yourtable(SSYS varchar(10),Material_Number varchar(100),Characteristic varchar(100),Description varchar(100))
INSERT INTO yourtable
VALUES('001','000000000001111','SH_DESC','TEST'),
('001','000000000001111','DESIGN_TYPE','NULL'),
('001','000000000001111','VOLTAGE','NULL'),
('001','000000000009999','SH_DESC','TEST2'),
('001','000000000009999','SH_DESC','LIGHT'),
('001','000000000009999','FILTER_TYPE','Filter element,Air'),
('001','000000000014560','SH_DESC','Horn,Signal'),
('001','000000000014560','DIMENSION_SIZE','NULL'),
('001','000000000014560','FILTER_TYPE ','NULL')
select max(SSYS),
max(Material_Number),
'SH_DESC' as Characteristic,
CASE WHEN SUM(CASE WHEN Characteristic='SH_DESC' and Description is not null then 1 else 0 end) = 1 then 1 else 0 end as cnt
from yourtable
group by Material_Number