SQL Server : "invalid column name" error after GROUP BY on subquery - sql

I am trying to perform the following query on a SQL Server database table with a GROUP BY on a column which results from a CASE statement done on a subquery:
SELECT
AVG(sales) as avg_sales,
COUNT(*) as total_sales,
CASE
WHEN (pay_method='cc') AND (user_type='subscriber') THEN 'cc-subscribed'
WHEN (pay_method='cash') AND (user_type='subscriber') THEN 'cash-subscribed'
ELSE 'standard'
END as user_payment_type
FROM
(SELECT
column1, column2,
UserType as user_type,
CASE
WHEN column1='something' AND column2='something_else' THEN 'cc'
WHEN column1='something_else' AND column2='something' THEN 'cash'
END as pay_method
FROM MyTable) b
GROUP BY
user_payment_type
The error I am getting is
MSSQLDatabaseException: (207, b"Invalid column name 'user_payment_type'.DB-Lib error message 20018, severity 16:\nGeneral SQL Server error: Check messages from the SQL Server\n")
Note that the column name user_payment_type is unique and does not already exist in MyTable.

SQL Server does not allow the use of this aliased column in the group by clause (others like MySql do allow it) because the group by clause is executed before select.
You have to use that case statement:
group by CASE
WHEN (pay_method='cc') AND (user_type='subscriber') THEN 'cc-subscribed'
WHEN (pay_method='cash') AND (user_type='subscriber') THEN 'cash-subscribed'
ELSE 'standard'
END

Your SELECT and GROUP BY should match. You could avoid duplicating code by using CROSS APPLY:
WITH cte AS (
SELECT column1,
column2,
UserType as user_type,
CASE
WHEN column1='something' AND column2='something_else' THEN 'cc'
WHEN column1='something_else' AND column2='something' THEN 'cash'
END as pay_method
FROM MyTable
)
SELECT AVG(c.sales) as avg_sales,
COUNT(*) as total_sales,
s.user_payment_type
FROM cte c
CROSS APPLY (SELECT CASE
WHEN (pay_method='cc') AND (user_type='subscriber') THEN 'cc-subscribed'
WHEN (pay_method='cash') AND (user_type='subscriber') THEN 'cash-subscribed'
ELSE 'standard' END) s(user_payment_type)
GROUP BY s.user_payment_type

As others have noted, you can't reference column aliases in the group by clause, but should reference the same expression there too.
Note, however, that you're performing two calculations on the same data. You could perform both calculations in the same subquery to make the query shorter and easier to maintain:
SELECT
AVG(sales) as avg_sales,
COUNT(*) as total_sales,
user_payment_type
FROM (
SELECT sales,
CASE
WHEN column1 = 'something' AND
column2 = 'something_else' AND /* These are the conditions for cc */
user_type = 'subscriber'
THEN 'cc-subscribed'
WHEN column1 = 'something_else' AND
column2 = 'something' AND /* conditions for cash */
user_type = 'subscriber'
THEN 'cash-subscribed'
ELSE 'standard'
END as user_payment_type
FROM MyTable
) b
GROUP BY
user_payment_type

A simple way to do this without nested subqueries uses apply:
SELECT v1.user_payment_type,
AVG(t.sales) as avg_sales,
COUNT(*) as total_sales
FROM MyTable t CROSS APPLY
(VALUES (CASE WHEN t.column1 = 'something' AND t.column2 = 'something_else' THEN 'cc'
WHEN t.column1 = 'something_else' AND t.column2 = 'something' THEN 'cash'
END
)
) v(pay_method) CROSS APPLY
(VALUES (CASE WHEN v.pay_method = 'cc' AND t.user_type = 'subscriber' THEN 'cc-subscribed'
WHEN v.pay_method = 'cash' AND t.user_type = 'subscriber' THEN 'cash-subscribed'
ELSE 'standard'
END)
) v1(user_payment_type)
GROUP BY v1.user_payment_type;
This allows you to define interdependent definitions without nesting subqueries or CTEs or repeating definitions.

Related

find duplicate row in the same table and mark them in sql

I have table 'workadress' and it contain 6 columns:
work_ref,work_street ,work_zip,workTN,...
I want to find duplicate rows in the same table depending on:
If (work_street, work_zip) are duplicate together, then you should look at workTN. If it is the same then put value ' ok ', but if workTN is not the same, put 'not ok'. How can I do it with SQL?
Result like:
You can use window functions:
select t.*,
(case when min(workTn) over (partition by work_street, work_zip) =
max(workTn) over (partition by work_street, work_zip)
then 'ok' else 'not ok'
end) as result
from t;
I think just a simple group by and count should be enough to do the job like so:
select
t.*,
case when dups.dups = 1 then 'OK' else 'not OK' end
from my_table t
join (
select work_street, work_zip, count(distinct workTN) dups
from my_table
group by work_street, work_zip
) dups on dups.work_street = t.work_street amd dups.work_zip = t.work_zip

SQL Select a specific value in the group

I have this following table
Dept---------- Sub_Dept---- Dept Type
Sales.............Advertising........A
Sales.............Marketing......... B
Sales.............Analytics.......... C
Operations.....IT..................... C
Operations.....Settlement........C
And the result should be if a department got a department type as A then change all record of that department to A, else keep it same
Dept---------- Sub_Dept---- Dept Type
Sales.............Advertising........A
Sales.............Marketing......... A
Sales.............Analytics.......... A
Operations.....IT..................... C
Operations.....Settlement........C
Anybody can give a suggestion on this? I thought of using the GROUP BY but have to output the Sub Department as well
Thanks a lot
I would do:
update t
set depttype = 'a'
where exists (select 1 from t t2 where t2.dept = t.dept and t2.dept = 'a') and
t.dept <> 'a';
If you just want a select, then do:
select t.*,
(case when sum(case when depttype = 'a' then 1 else 0 end) over (partition by dept) > 1
then 'a'
else depttype
end) as new_depttype
from t;
Use below query
select a11.dept, a12.Sub_Dept, (case when a12.min_dep_type='A' then 'A' else a11.dep_type) as dep_type
from tab a11
JOIN (select dept, min(dep_type) min_dep_type from tab group by dept) a12
on a11.dept = a12.dept
Try this:
update table
set depttype= case when dept in (select dept from table where depttype='a') then 'a' else depttype end
This should work:
select a.dept, a.sub_dept,
case when b.dept is not null then 'A' else dept_type end as dept_type
from aTable a
left join(
select distinct Dept from aTable where dept_type = 'A'
)
b on b.dept = a.dept
You could use analytic functions to check whether exists the specific value in the group.
Try below query:
SELECT t.Dept,
t.Sub_Dept,
NVL(MIN(CASE WHEN t.Dept_Type = 'A'
THEN Dept_Type END) OVER (PARTITION BY t.Dept), t.Dept_Type) AS Dept_Type
FROM table_1 t
Using the analytic function MIN(), you can search for the value of 'A' (if it does exist inside the group). MIN works for non-null values only, so if you don't have any 'A' in the group, the result will be NULL.
At this point, you can use NVL to choose whether to print the value found in the group or the actual dept_type of the row.

SQL Group By with Substr

I am trying to group by with substring and I know I cannot use an alias but even like this, it is not producing any results.
select
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK'
group by substr(cd_orig_bic,5,2);
You don't have any aggregation function in your query.
Here is how "group by" works:
SELECT sum(column1) -- here is a aggregation function
,column2
FROM table
GROUP BY column2 -- here is the column you want to aggregate on
The result is the sum of column1 for each value of column 2
You can read this article for more information.
Why do you want to group by the data as there is no aggregate function used in the query.
If you want unique values you could have distinct values using distinct keyword.
select distinct
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK';
Also if you want to have aggregate function you could have above query as inner and use group by in the outer query.
select cd,txn_type,d_booking,sum(d_value) as value,
d_execution from (select
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK'
) group by cd,txn_type,d_booking,d_execution;

Aggregate function calls may not be nested ERROR

I am trying to run this query in postgresql but getting this error: "ERROR: Aggregate function calls may not be nested". I am not sure what the problem or a solution for this.
Here is my query:
select a.KEY_NBR, a.MAIN_ID
,
case
when a.KEY_NBR = a.MAIN_ID
then 'Don''t Use'
when a.count = MAX(a.count) over(partition by a.KEY_NBR)
then 'Good'
else 'Bad'
end [flag]
from MYTABLE a
Your query looks ok and should work. Perhaps the version you use has some limitations. Try this variation:
WITH a AS
( SELECT key_nbr, main_id, count,
MAX(count) OVER (PARTITION BY key_nbr) AS max_count
FROM mytable
)
SELECT a.key_nbr, a.main_id,
CASE WHEN a.key_nbr = a.main_id
THEN 'Don''t Use'
WHEN a.count = max_count
THEN 'Good'
ELSE 'Bad'
END AS flag
FROM a ;

A better way of writing this particular SELECT query in a VIEW

I would like to know if there is a better way of writing the SELECT clause in the query below. There are three possible cases for status. It can either have a value of 'A' or 'N' or null.
I think that placing the AND status = 'A' in the where clause is redundant when you are comparing the subTable values to a value of 'A'. Is there any way that this could be rewritten more efficiently? I feel like restricting the status in the WHERE clause is always going to give you an 'A' and hence pointless to do the CASE statement.
ALTER VIEW dbo.st_review_status_vw AS
(
SELECT c.st_id, c.ms_price_comp_fy,
CASE
WHEN (SELECT status FROM
(SELECT st_id, status, ms_price_comp_fy
FROM ms_price_comp
WHERE st_id = c.st_id
AND ms_price_comp_fy = c.ms_price_comp_fy
AND status = 'A'
GROUP BY st_id, status, ms_price_comp_fy)
AS subTable) = 'A' THEN 'C'
ELSE 'I'
END AS status,
MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)
try this
ALTER VIEW dbo.st_review_status_vw AS (
SELECT c.st_id, c.ms_price_comp_fy,
CASE WHEN MIN(status) = 'A' THEN 'C'
ELSE 'I'
END AS status,
MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)
This looks to be the same to me
ALTER VIEW dbo.st_review_status_vw AS (
SELECT
c.st_id
, c.ms_price_comp_fy
, CASE
WHEN MIN(status) = 'A' THEN 'C'
ELSE 'I'
END AS status
, MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)