COUNT from DISTINCT values in multiple columns - sql

If this has been asked before, I apologize, I wasn't able to find a question/solution like it before breaking down and posting. I have the below query (using Oracle SQL) that works fine in a sense, but not fully what I'm looking for.
SELECT
order_date,
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
srt AS srt_level,
COUNT(*) AS total_orders
FROM
database.t_con
WHERE
order_date IN (
'&Enter_Date_YYYYMM'
)
GROUP BY
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
srt,
order_date
ORDER BY
p_category,
issue_group,
srt_level,
order_date
Current Return (12 rows):
Needed Return (8 rows without the tan rows being shown):
Here is the logic of total_order column that I'm expecting:
count of order_date where (srt_level = 80 + 100 + Late) ... 'Late' counts needed to be added to the total, just not be displayed
I'm eventually adding a filled_orders column that will go before the total_orders column, but I'm just not there yet.
Sorry I wasn't as descriptive earlier. Thanks again!

You don't appear to need a subquery; if you want the count for each combination of values then group by those, and aggregate at that level; something like:
SELECT
t1.order_date,
t1.p_category,
CASE
WHEN ( t1.issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
t1.srt AS srt_level,
COUNT(*) AS total_orders
FROM
database.t_con t1
WHERE
t1.order_date = TO_DATE ( '&Enter_Date_YYYYMM', 'YYYYMM' )
GROUP BY
t1.p_category,
CASE
WHEN ( t1.issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
t1.srt,
t1.order_date
ORDER BY
p_category,
issue_group,
srt_level,
order_date;
You shouldn't be relying on implicit conversion and NLS settings for your date argument (assuming order_date is actually a date column, not a string), so I've used an explicit TO_DATE() call, using the format suggested by your substitution variable name and prompt.
However, that will give you the first day of the supplied month, since a day number isn't being supplied. It's more likely that you either want to prompt for a full date, or (possibly) just the year/month but want to include all days in that month - which IN() will not do, if that was your intention. It also implies that stored dates all have their time portions set to midnight, as that is all it will match on. If those values have non-midnight times then you need a range to pick those up too.

I got it working to the extent of what my question was. Just needed to nest each column where counts/calculations were happening.
SELECT
order_date,
p_category,
issue_group,
srt_level,
order_count,
SUM(order_count) OVER(
PARTITION BY order_date, issue_group, p_category
) AS total_orders
FROM
(
SELECT
order_date,
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END AS issue_group,
srt AS srt_level,
COUNT(*) AS order_count
FROM
database.t_con
WHERE
order_date IN (
'&Enter_Date_YYYYMM'
)
GROUP BY
p_category,
CASE
WHEN ( issue_grp = 1 ) THEN '1'
ELSE '2/3 '
END,
srt,
order_date
)
ORDER BY
order_date,
p_category,
issue_group

Related

SQL Case Statements with Multiple Max Conditions

I currently am working with two conditions that I would like to combine into one, but ran into some trouble. I have a dataset that includes quantity and date. I have created a date flag in the form of a case statement that flags whether it is the last day of the week, and gives it a "Y" or "N". The end result that I need is what that last DATE of the week.
My end result/goal is Column D
Here is my current source code:
select
pos.quantity_on_hand,
d.cal_date,
case
when date_key in( Select max(date_key) from edw.D_dates group by fiscal_year_nbr, fiscal_week_nbr)
then 'Y'
else 'N'
end Week_end_flag
from
edw.f_pos_daily pos,
edw.d_dates d,
where
pos.pos_date_key = d.date_key
I then create another custom column in PowerBI Desktop that looks like this:
This is what I used for my column calculation:
Last Inventory Date = RETURN(CALCULATE(MAXX(Inventory, Inventory[Cal_date]), filter ('D_Dates', 'D_Dates'[Week_end_flag]="Y")).
I tried to combine them into one, with something like this, but have failed:
case
when date_key in( Select max(date_key) from edw.D_dates group by fiscal_year_nbr, fiscal_week_nbr)
then MAX (cal_date) from edw.D_Dates where cal_date< current_date AS 'yyyy-mm-dd'
else 'N'
end Week_End_flag
Use the select command inside the then clause
change this line:
then MAX (cal_date) from edw.D_Dates where cal_date< current_date AS 'yyyy-mm-dd'
to:
then (SELECT MAX (cal_date) from edw.D_Dates where cal_date< current_date AS 'yyyy-mm-dd')
complete code:
select
pos.quantity_on_hand,
d.cal_date,
case
when date_key in( Select max(date_key) from edw.D_dates group by fiscal_year_nbr, fiscal_week_nbr)
then (SELECT MAX (cal_date) from edw.D_Dates where cal_date< current_date AS 'yyyy-mm-dd')
else 'N'
end Week_end_flag
from
edw.f_pos_daily pos,
edw.d_dates d,
where
pos.pos_date_key = d.date_key

SQL - values from two rows into new two rows

I have a query that gives a sum of quantity of items on working days. on weekend and holidays that quantity value and item value is empty.
I would like that on empty days is last known quantity and item.
My query is like this:
`select a.dt,b.zaliha as quantity,b.artikal as item
from
(select to_date('01-01-2017', 'DD-MM-YYYY') + rownum -1 dt
from dual
connect by level <= to_date(sysdate) - to_date('01-01-2017', 'DD-MM-YYYY') + 1
order by 1)a
LEFT OUTER JOIN
(select kolicina,sum(kolicina)over(partition by artikal order by datum_do) as zaliha,datum_do,artikal
from
(select sum(vv.kolicinaulaz-vv.kolicinaizlaz)kolicina,vz.datum as datum_do,vv.artikal
from vlpzaglavlja vz, vlpvarijante vv
where vz.id=vv.vlpzaglavlje
and vz.orgjed='01006'
and vv.skladiste='01006'
and vv.artikal in (3069,6402)
group by vz.datum,vv.artikal
order by vv.artikal,vz.datum asc)
order by artikal,datum_do asc)b
on a.dt=b.datum_do
where a.dt between to_date('12102017','ddmmyyyy') and to_date('16102017','ddmmyyyy')
order by a.dt`
and my output is like this:
and I want this:
In short, if quantity is null use lag(... ignore nulls) and coalesce or nvl:
select dt, item,
nvl(quantity, lag(quantity ignore nulls) over (partition by item order by dt))
from t
order by dt, item
Here is the full query, I cannot test it, but it is something like:
with t as (
select a.dt, b.zaliha as quantity, b.artikal as item
from (
select date '2017-10-10' + rownum - 1 dt
from dual
connect by date '2017-10-10' + rownum - 1 <= date '2017-10-16' ) a
left join (
select kolicina, datum_do, artikal,
sum(kolicina) over(partition by artikal order by datum_do) as zaliha
from (
select sum(vv.kolicinaulaz-vv.kolicinaizlaz) kolicina,
vz.datum as datum_do, vv.artikal
from vlpzaglavlja vz
join vlpvarijante vv on vz.id = vv.vlpzaglavlje
where vz.orgjed = '01006' and vv.skladiste='01006'
and vv.artikal in (3069,6402)
group by vz.datum, vv.artikal)) b
on a.dt = b.datum_do)
select *
from (
select dt, item,
nvl(quantity, lag(quantity ignore nulls)
over (partition by item order by dt)) qty
from t)
where dt >= date '2017-10-12'
order by dt, item
There are several issues in your query, major and minor:
in date generator (subquery a) you are selecting dates from long period, january to september, then joining with main tables and summing data and then selecting only small part. Why not filter dates at first?,
to_date(sysdate). sysdate is already date,
use ansi joins,
do not use order by in subqueries, it has no impact, only last ordering is important,
use date literals when defining dates, it is more readable.

CASE Statement inside a subquery

I was able to create the following query after help from the post below
select * from duppri t
where exists (
select 1
from duppri
where symbolUP = t.symbolUP
AND date = t.date
and price <> t.price)
ORDER BY date
SQL to check when pairs don't match
I have now realized that I need to add a case statement to indicate when all the above criteria fits, but the type value is equal between duppri and t.duppri. This occurs because of case sensitivity. This query is an attempt to clean up a portfolio accounting system that unfortunately allowed numerous duplicates because it didn't have strong referential integrity or constraints.
I would like the case statement to produce the column 'isMatch'
Date |Type|Symbol |SymbolUP |Concatt |Price |IsMatch
6/30/1995 |gaus|313586U72|313586U72|gaus313586U72|109.25|Different
6/30/1995 |gbus|313586U72|313586U72|gbus313586U72|108.94|Different
6/30/1995 |agus|SRR |SRR |agusSRR |10.25 |Different
6/30/1995 |lcus|SRR |SRR |lcusSRR |0.45 |Different
11/27/1996|lcus|LLY |LLY |lcusLLY |76.37 |Matched
11/27/1996|lcus|lly |LLY |lcusLLY |76 |Matched
11/28/1996|lcus|LLY |LLY |lcusLLY |76.37 |Matched
11/28/1996|lcus|lly |LLY |lcusLLY |76 |Matched
I tried the following CASE statement but it is creating errors
SELECT * from duppri t
where exists (
select 1,
CASE IsMatch WHEN [type] = [t.TYPE] THEN 'Matched' ELSE 'Different' END
from duppri
where symbolUP = t.symbolUP
AND date = t.date
and price <> t.price)
ORDER BY date
You could just use window functions, if I understand correctly:
select d.*,
(case when mint = maxt
then 'Matched' else 'Different'
end)
from (select d.*,
min(type) over (partition by symbolup, date) as mint,
max(type) over (partition by symbolup, date) as maxt,
min(price) over (partition by symbolup, date) as minp,
max(price) over (partition by symbolup, date) as maxp
from duppri d
) d
where minp <> maxp
order by date;
The subquery used with the exists predicate can't and won't return anything other than true/false but you can accomplish what you want using a subquery like this, which should work:
select
*,
(select
CASE when count(distinct type) = 1 THEN 'Matched' ELSE 'Different' END
from duppri
where symbol = t.symbol and date = t.date
) IsMatch
from duppri t
where exists (
select 1
from duppri
where symbol = t.symbol
and price <> t.price);

SQL get number of hours on previous rows

I am working on a query that extracts information about a store opening and close time. This is the resultset:
RTL_LOC_ID TRANS_TYPCODE BEGIN_DATETIME
---------- ------------------------------ ---------------------------
2390 WORKSTATION_OPEN 14.10.01 09:53:43,121000000
2390 WORKSTATION_CLOSE 14.10.01 23:51:49,729000000
2390 WORKSTATION_OPEN 14.10.02 09:57:47,768000000
2390 WORKSTATION_CLOSE 14.10.02 23:47:00,120000000
2390 WORKSTATION_OPEN 14.10.03 09:47:38,949000000
2390 WORKSTATION_CLOSE 14.10.03 23:45:42,602000000
6 rows selected
This is the query:
SELECT RTL_LOC_ID,TRANS_TYPCODE, BEGIN_DATETIME
FROM TRN_TRANS
WHERE(trans_typcode = 'WORKSTATION_OPEN' OR trans_typcode='WORKSTATION_CLOSE')
AND BUSINESS_DATE BETWEEN '14.10.01 00:00:00' AND '14.10.03 00:00:00'
ORDER BY BUSINESS_DATE, BEGIN_DATETIME ASC;
So I need to calculate the number of hours between the opening and closing of the store and place that value into a new column. I would also like to put the result for the day in the same row instead of two separate lines for each day.
This answer assumes MySQL since the question was not tagged with Oracle to begin with. I'm leaving this answer here, since it might inspire someone with Oracle skills toward a solution...
Assuming a location always opens before it closes, a quick and dirty solution could look like this:
SELECT RTL_LOC_ID, DATE(BUSINESS_DATE),
MIN(BUSINESS_DATE) AS [OpenTime],
MAX(BUSINESS_DATE) AS [CloseTime]
FROM
TRN_TRANS
WHERE(trans_typcode = 'WORKSTATION_OPEN' OR trans_typcode='WORKSTATION_CLOSE')
AND BUSINESS_DATE BETWEEN '14.10.01 00:00:00' AND '14.10.03 00:00:00'
GROUP BY RTL_LOC_ID, DATE(BUSINESS_DATE)
ORDER BY DATE(BUSINESS_DATE)
Or if you want to be pedantic:
SELECT RTL_LOC_ID, DATE(BUSINESS_DATE),
MAX(CASE trans_typcode WHEN 'WORKSTATION_OPEN' THEN BUSINESS_DATE ELSE NULL END) AS [OpenTime],
MAX(CASE trans_typcode WHEN 'WORKSTATION_CLOSE' THEN BUSINESS_DATE ELSE NULL END) AS [CloseTime],
FROM
-- rest of query same as above --
Is this what you mean?
SELECT
TRN_TRANS.RTL_LOC_ID,
DATE_FORMAT(BEGIN_DATETIME ,'%Y-%m-%d') AS _date,
TIMEDIFF(closing_time.BEGIN_DATETIME , opening_time.BEGIN_DATETIME ) AS _hours
FROM TRN_TRANS
INNER JOIN
(
SELECT RTL_LOC_ID, BEGIN_DATETIME, DATE_FORMAT(BEGIN_DATETIME ,'%Y-%m-%d') as _date
FROM TRN_TRANS
WHERE TRANS_TYPCODE = 'WORKSTATION_OPEN'
) AS opening_time
ON
TRN_TRANS.RTL_LOC_ID = opening_time.RTL_LOC_ID
AND
DATE_FORMAT(TRN_TRANS.BEGIN_DATETIME ,'%Y-%m-%d') = opening_time._date
INNER JOIN
(
SELECT RTL_LOC_ID, BEGIN_DATETIME, DATE_FORMAT(BEGIN_DATETIME ,'%Y-%m-%d') as _date
FROM TRN_TRANS
WHERE TRANS_TYPCODE = 'WORKSTATION_CLOSE'
) AS closing_time
ON
TRN_TRANS.RTL_LOC_ID = closing_time.RTL_LOC_ID
AND
DATE_FORMAT(TRN_TRANS.BEGIN_DATETIME ,'%Y-%m-%d') = closing_time._date
GROUP BY TRN_TRANS.RTL_LOC_ID, _date, _hours

Oracle Select Query, distributing frequency of labels

If i know how much data i am receiving, i can distribute the appearance of a label in this way:
select
value_column
, CASE WHEN TO_CHAR(VALUE_DATE, 'mm') = '01' and MOD(extract(year from VALUE_DATE), 3) = 0
THEN TO_CHAR(VALUE_DATE, 'MON-yyyy')
else ' '
END VALUE_DATE_STRING
from SomeTable
this will show the data label on January every 3rd year.
Now, if i don't know how many years are coming back, i'd like to figure this out in the same select and display a total of 5 labels.
i reckon i'd need something like this (pseudo code):
CASE WHEN MOD(allRows / 5, ROW_NUM) = 0
i guess the only challenging part is getting the allRows in the same select.. since i'm calling this sql from a telerik report, there's limited support for declaring vars and running multiple statements..
This might do what you want:
select value_column,
(CASE WHEN mod(rownum, trunc(cnt / 5)) = 0 o
THEN TO_CHAR(VALUE_DATE, 'MON-yyyy')
else ' '
END) as VALUE_DATE_STRING
from (select t.*, count(*) over () as cnt
from SomeTable t
) t;
It might be trunc((cnt - 1) / 5).
DIT:
If this does what you want, you don't need a CTE or subquery. I just thought that it made more sense this way (and the subquery does not effect performance). You can do:
select value_column,
(CASE WHEN mod(row_number() over (order by value_date),
trunc(count(*) over () / 5)) = 0
THEN TO_CHAR(VALUE_DATE, 'MON-yyyy')
else ' '
END) as VALUE_DATE_STRING
from SomeTable t;
By the way, you should include order by if you are using rownum. Oracle does not guarantee the ordering of rows in a result set without an order by.
here's my solution.. it could still be optimized, and i believe the rounding will not always work in my favor..
WITH base as (
select
value_column
, value_date
, cnt.c
, case mod(c,2) when 0 then 6 else 5 end as divider -- get 5 or 6 dates.. try to minimize truncation consequences.. this may need work.
, row_number() over (order by value_date) as row_number
from MyView
left join (select count(*) c from MyView where id = 250170) cnt on 1=1
where id = 250170
order by value_date
)
select
value_column
, value_date
, CASE WHEN MOD(row_number, trunc(c/divider)) = 0
THEN TO_CHAR(VALUE_DATE, 'MON-yyyy')
else ' '
END VALUE_DATE_STRING
from base
order by value_date
UPDATE
simplified based on Gordon's answer
select
value_column
, value_date
, (
CASE WHEN
mod(
row_number() over (order by value_date),
trunc(count(*) over () / 5)) = 0
THEN TO_CHAR(VALUE_DATE, 'MON-yyyy')
else ' '
END) as LABEL_STRING
from MyView