sqlite select: grouping in one line, 12 entries - sql

I have a table with fields:
Client_ID, Date, Value
where there is an entry for each of the 12 months of a year (i.e., 12 entries for each client).
I would like to create a table with just one row per Client_ID that contains all the values from each months. Something like:
Client_ID, Date_January, Value_January, Date_February, Value_February, ........, Date_December, Value_December
Can anyone help me with the query?
This is what I'm trying to do (not working...):
select
Client_Id,
case when ((strftime('%m', Date) = '01')) then
Date as Date_January,
Value as Value_January,
else null end
case when ((strftime('%m', Date) = '01')) then
Date as Date_February,
Value as Value_February,
else null end
....
from Test_Table
where
strftime('%Y', Date) = '2013'
;

First you need to untangle your case constructs as they generate a single value. Use:
case
when ((strftime('%m', Date) = '01')) then Date
else null
end as Date_January,
case when ((strftime('%m', Date) = '01')) then Value
else null
end as Value_January,
Then, if you want one row per client, use GROUP BY ClientID.
The third issue is how to aggregate all the Date_January columns into one row. If you really know that there is exactly one row per month per client, you can use MAX() knowing that the not null value will be higher than the NULL values:
select
Client_Id,
MAX(case
when ((strftime('%m', Date) = '01')) then Date
else null
end) as Date_January,
MAX(case when ((strftime('%m', Date) = '01')) then Value
else null
end) as Value_January,
MAX(case
when ((strftime('%m', Date) = '02')) then Date
else null
end) as Date_February,
MAX(case when ((strftime('%m', Date) = '02')) then Value
else null
end) as Value_February,
....
from Test_Table
where
strftime('%Y', Date) = '2013'
group by Client_Id;

Related

How to nest multiple case when expressions and add a condition

I am trying to divide customers (contact_key) column who shopped in 2021 (A.TXN_MTH) into new and 'returning' with returning meaning that they had not shopped in the last 12 months (YYYYMM in X.Fiscal_mth_idnt column).
I am using CASE WHEN A.TXN_MTH = MIN(X.FISCAL_MTH_IDNT) THEN 'NEW' which is correct. The next case when should be when the max month before X.TXN_MTH is 12 or more months previous. I have added the 12 months part in the Where statement. Should I be nesting 3 CASE WHEN'S instead of WHERE?
SELECT
T.CONTACT_KEY
, A.TXN_MTH
, CASE WHEN A.TXN_MTH = MIN(X.FISCAL_MTH_IDNT) THEN 'NEW'
WHEN (MAX(CASE WHEN X.FISCAL_MTH_IDNT < A.TXN_MTH THEN X.FISCAL_MTH_IDNT ELSE NULL END)) THEN 'RETURNING'
END AS CUST_TYPE
FROM B_TRANSACTION T
INNER JOIN B_TIME X
ON T.TRANSACTION_DT_KEY = X.DATE_KEY
INNER JOIN A
ON A.CONTACT_KEY = T.CONTACT_KEY AND A.BU_KEY = T.BU_KEY
WHERE (MAX(CASE WHEN X.FISCAL_MTH_IDNT < A.TXN_MTH THEN X.FISCAL_MTH_IDNT ELSE NULL END)) < A.TXN_MTH - (date_format(add_months(concat_ws('-',substr(yearmonth,1,4),substr(yearmonth,5,2),'01'),-12),'yyyyMM')
GROUP BY
T.CONTACT_KEY
, TXN_MTH;
You have not described your tables, so assuming fiscal_mth_idnt is a DATE column then you can use the LAG analytic function to find the previous row's value:
SELECT contact_key,
txn_mth,
CASE
WHEN prev_fiscal_mth_idnt IS NULL
THEN 'NEW'
WHEN ADD_MONTHS(prev_fiscal_mth_idnt, 12) < fiscal_mth_idnt
THEN 'RETURNING'
ELSE 'CURRENT'
END AS cust_type
FROM (
SELECT T.CONTACT_KEY,
A.TXN_MTH,
yearmonth,
X.FISCAL_MTH_IDNT,
LAG(X.FISCAL_MTH_IDNT) OVER (
PARTITION BY T.CONTACT_KEY
ORDER BY X.FISCAL_MTH_IDNT
) AS prev_fiscal_mth_idnt
FROM B_TRANSACTION T
INNER JOIN B_TIME X
ON T.TRANSACTION_DT_KEY = X.DATE_KEY
INNER JOIN A
ON A.CONTACT_KEY = T.CONTACT_KEY AND A.BU_KEY = T.BU_KEY
)
WHERE yearmonth LIKE '2021%';

How select second max value from a subquery

I need to select a max date (DATA_RIF_PATR_KU) for the key PROT, CODICE_COM, but there are also 9999-12-31 values. So when there are others date than 9999-12-31 I need to select the "real max date" else 9999-12-31.
For example:
For the combination:
PROT = '202000060300' AND
CODICE_COM='Z01'
I have 2 dates of DATA_RIF_PATR_KU: 03-11-20, 9999-12-31. I need to select the first one.
I wrote this code:
'''
SELECT
A.GRADO,
A.COMM,
A.PROT,
MAX(A.FLAG_VAL_IND) AS FLAG_VAL_IND,
SUM(A.CONT_VERS) AS CONT_VERS,
SUM(A.IMP_PREN) AS IMP_PREN,
SUM(A.CONT_DIFF) AS CONT_DIFF,
MIN(A.DATA_VER) AS DATA_VER,
MAX(A.PREN_DEB) AS PREN_DEB,
MAX(A.PREN_DEB_VER) AS PREN_DEB_VER,
MAX(A.GRAT_PATR_KU) AS GRAT_PATR_KU,
MAX(CASE WHEN (A.MAX_DATA_RIF_PATR_NON_9999)='SI' THEN A.DATA_RIF_PATR_KU
WHEN (A.MAX_DATA_RIF_PATR_NON_9999_2)='SI2' THEN A.DATA_RIF_PATR_KU END) AS DATA_RIF_PATR_KU,
SUM(A.MAGG_PEC) AS MAGG_PEC,
MAX(A.ASS_PEC) AS ASS_PEC,
MAX(A.ASS_CF) AS ASS_CF,
MAX(A.ASS_VAL) AS ASS_VAL,
MAX(A.FLAG_LAV) AS FLAG_LAV,
SUM(A.CONT_DIC) AS CONT_DIC,
SUM(A.CONT_TOT) AS CONT_TOT,
SUM(A.CONT_DIFF_VERIF) AS CONT_DIFF_VERIF,
MAX(A.DATA_AGG) AS DATA_ULTIMA_VALIDAZIONE,
MAX(CASE WHEN (A.DATA_INV)<'9999-12-31'
THEN '1'
ELSE '0' END) AS PRESENZA_INVITO_PAG,
MAX(CASE WHEN (A.DATA_VERS_INV)<'9999-12-31'
THEN '1'
ELSE '0' END) AS PRESENZA_VERS_INVITO
FROM
(
SELECT
distinct BILS05_GRADO AS GRADO,
BILS05_CODICE_COM AS COMM,
BILS05_PROT AS PROT,
CASE WHEN BILS05_FLAG_VAL_IND IN ('0','9')
THEN 'D'
ELSE 'I' END AS FLAG_VAL_IND,
bils05_tipo_doc, bils05_prog_alleg, bils05_pren_deb as pren_deb,
BILS05_PREN_DEB_VERIF AS PREN_DEB_VER,
CASE WHEN BILS05_PREN_DEB='1' AND BILS05_PREN_DEB_VERIF='1'
THEN 0
ELSE BILS05_CONT_VERS_VERIF END AS CONT_VERS,
CASE WHEN BILS05_PREN_DEB='1' AND BILS05_PREN_DEB_VERIF='1'
THEN BILS05_CONT_VERS_VERIF
ELSE 0 END AS IMP_PREN,
BILS05_CONT_DIFF AS CONT_DIFF,
BILS05_DATA_ACQ_KU AS DATA_VER,
BILS05_MAGG_PEC AS MAGG_PEC,
BILS05_ASS_PEC AS ASS_PEC,BILS05_ASS_CF AS ASS_CF,BILS05_ASS_VAL AS ASS_VAL,
BILS05_FLAG_LAV AS FLAG_LAV,
BILS05_CONT_VERS AS CONT_DIC,
BILS05_CONT_TOT AS CONT_TOT,
BILS05_CONT_DIFF_VERIF AS CONT_DIFF_VERIF,
BILS05_DATA_AGG_KU AS DATA_AGG,
BILS05_DATA_INV_PAG AS DATA_INV,
BILS05_DATA_VERS_INV AS DATA_VERS_INV,
BILS05_GRAT_PATR as GRAT_PATR_KU,
BILS05_DATA_RIF_PATR AS DATA_RIF_PATR_KU,
ROW_NUMBER() OVER(PARTITION BY BILS05_PROT, BILS05_CODICE_COM ORDER BY BILS05_DATA_RIF_PATR) AS RN,
LEAD(DATA_RIF_PATR_KU) OVER(PARTITION BY BILS05_PROT, BILS05_CODICE_COM ORDER BY DATA_RIF_PATR_KU) AS FOLLOW_DATA,
LAG(DATA_RIF_PATR_KU) OVER(PARTITION BY BILS05_PROT, BILS05_CODICE_COM ORDER BY DATA_RIF_PATR_KU) AS PREV_DATA,
CASE WHEN (DATA_RIF_PATR_KU < FOLLOW_DATA AND DATA_RIF_PATR_KU NOT = '9999-12-31' AND FOLLOW_DATA='9999-12-31' )THEN 'SI'
ELSE 'NO' END AS MAX_DATA_RIF_PATR_NON_9999,
CASE WHEN (DATA_RIF_PATR_KU NOT = '9999-12-31' AND FOLLOW_DATA IS NULL) OR (DATA_RIF_PATR_KU = '9999-12-31' AND FOLLOW_DATA IS NULL)
THEN 'SI2' ELSE 'NO' END AS MAX_DATA_RIF_PATR_NON_9999_2
FROM ZUCOW.BILS05
WHERE
BILS05_FLAG_LAV='2'
) A
WHERE
A.PROT='202000060300' AND
A.COMM='Z01'
GROUP BY A.GRADO, A.COMM, A.PROT;
'''
but it doesn't work properly. It select 9999-12-31 instead of 03-11-20 in the above example
Your example is rather long and complex, but the general idea would be
(assuming DATA_RIF_PATR_KU is a date field):
select
max(case when DATA_RIF_PATR_KU='9999-12-31' then null else DATA_RIF_PATR_KU end),
PROT, CODICE_COM
FROM FROM ZUCOW.BILS05
GROUP BY 2,3
yes that's the general idea.. Anyway at the end, I solved it in another way:
MAX(CASE WHEN TO_CHAR(A.DATA_RIF_PATR_KU, 'YYYY-MM-DD' ) LIKE ('9999-12-31')THEN '0000-12-31' ELSE TO_CHAR(A.DATA_RIF_PATR_KU, 'YYYY-MM-DD') END) AS DATA_RIF_PATR_KU_STR
So, I converted the 9999-12-31 values in 0000-12-31 (I had to to transform the date value in string), so then I could do the max and then I converted the values back in the ETL software.
Thank You all for helping

CASE WHEN condition with MAX() function

There are a lot questions on CASE WHEN topic, but the closest my question is related to this How to use CASE WHEN condition with MAX() function query which has not been resolved.
Here is some of my sample data:
date
debet
2022-07-15
57190.33
2022-07-14
815616516.00
2022-07-15
40866.67
2022-07-14
1221510.00
So, I want to all records for the last two dates and three additional columns: sum(sales) for the previous day, sum for the current day and the difference between them:
SELECT
[debet],
[date] ,
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END ) AS sum_act,
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END ) AS sum_prev ,
(
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END )
-
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END )
) AS diff
FROM
Table
WHERE
[date] = ( SELECT MAX(date) FROM Table WHERE date < ( SELECT MAX(date) FROM Table) )
OR
[date] = ( SELECT MAX(date) FROM Table WHERE date = ( SELECT MAX(date) FROM Table ) )
GROUP BY
[date],
[debet]
Further, of course, it informs that I can't use the aggregate function inside CASE WHEN. Now I use this combination: sum(CASE WHEN [date] = dateadd(dd,-3,cast(getdate() as date)) THEN [debet] ELSE 0 END). But here every time I need to make an adjustment for weekends and holidays. The question is, is there any other way than using 'getdate' in 'case when' Statement to get max date?
Expected result:
date
sum_act
sum_prev
diff
2022-07-15
97190.33
0.00
97190.33
2022-07-14
0.00
508769.96
-508769.96
You can use dense_rank() to filter the last 2 dates in your table. After that you can use either conditional case expression with sum() to calculate the required value
select [date],
sum_act = sum(case when rn = 1 then [debet] else 0 end),
sum_prev = sum(case when rn = 2 then [debet] else 0 end),
diff = sum(case when rn = 1 then [debet] else 0 end)
- sum(case when rn = 2 then [debet] else 0 end)
from
(
select *, rn = dense_rank() over (order by [date] desc)
from tbl
) t
where rn <= 2
group by [date]
db<>fiddle demo
Two steps:
Get the sums for the last three dates
Show the results for the last two dates.
Well, we could also get all daily sums in step 1, but we just need the last three in order to calculate the sums for the last two days, so why aggregate more data than necessary?
Here is the query. You may have to put the date column name in brackets in SQL Server, as date is a keyword in SQL.
select top(2)
date,
sum_debit_current,
sum_debit_previous,
sum_debit_current - sum_debit_previous as diff
(
select
date,
sum(debet) as sum_debit_current,
lag(sum(debet)) over (order by date) as sum_debit_previous
from table
where date in (select distinct top(3) date from table order by date desc)
group by date
)
order by date desc;
(SQL Server uses TOP(n) instead of standard SQL FETCH FIRST 3 ROWS and while SELECT DISTINCT TOP(3) date looks like "get the top 3 rows, then apply distinct on their date", it is really "apply distinct on the dates, then get the top 3" like in standard SQL.)

Conditional CASE WHEN select snowflake SQL

I am stuck on a conditional snowflake select sql. I am trying to count the IDs when they have the corresponding categorial value. I would appreciate some help.
Thanks
SELECT
YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT(CASE WHEN ID THEN CATEGORY = 'A')
from table
group by week, year;
Here is one method:
SELECT YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
SUM(CASE WHEN CATEGORY = 'A' THEN 1 ELSE 0 END) as num_a
FROM table
GROUP BY week, year;
Snowflake supports COUNT_IF:
Returns the number of records that satisfy a condition.
Aggregate function
COUNT_IF( <condition> )
SELECT YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT_IF(CATEGORY = 'A') AS num_a
FROM tab
GROUP BY week, year;
You should / can use IFF() since case when is more suitable when there are multiple conditions.
SELECT
YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT(IFF(CATEGORY = 'A',ID,NULL)) as count
from table
group by week, year;
COUNT() counts the number of rows that are not null.
If you are want when ID is not null AND CATEGORY = 'A' then
COUNT(CASE WHEN ID IS NOT NULL AND CATEGORY = 'A' THEN TRUE ELSE NULL END)
will give you that, or you can use a SUM like in Gordon's answer
SUM(CASE WHEN ID IS NOT NULL AND CATEGORY = 'A' THEN 1 ELSE 0 END)
or you can use the snowflake IFF as a shorter form for the same thing, which is how I do it
SUM( IFF( ID IS NOT NULL AND CATEGORY = 'A', 1, 0))

Sql out put which come into multiple row converted to single row

This query give multiple row which needs to be shown in single row. Please help.
SELECT blng_serv_code, (COUNT (blng_serv_code)) AS total ,
DECODE (package_trx_yn, 'Y', 'PKG', 'N', 'NPKG') pkg_status FROM bl_patient_charges_folio
WHERE operating_facility_id = 'MC'
AND trx_date >= TO_DATE ('10/10/2019 00:00:00', 'MM/DD/YYYY HH24:MI:SS')AND blng_serv_code = 'LBSB000015'
GROUP BY blng_serv_code, package_trx_yn
If you want the value in a single row, leave out the package status:
SELECT blng_serv_code, COUNT(*) AS total
FROM bl_patient_charges_folio
WHERE operating_facility_id = 'MC' AND
trx_date >= DATE '2019-10-10' AND
blng_serv_code = 'LBSB000015'
GROUP BY blng_serv_code;
If you do want the package status, then you need to explain the logic for including it "on a single row".
EDIT:
It sounds like you want the values in separate columns:
SELECT blng_serv_code, COUNT(*) AS total,
SUM(CASE WHEN package_trx_yn = 'Y' THEN 1 ELSE 0 END) as pkg_cnt,
SUM(CASE WHEN package_trx_yn = 'N' THEN 1 ELSE 0 END) as npkg_cnt
FROM bl_patient_charges_folio
WHERE operating_facility_id = 'MC' AND
trx_date >= DATE '2019-10-10' AND
blng_serv_code = 'LBSB000015'
GROUP BY blng_serv_code;