Roll up and Cube operator in access (Summary Queries) - database

Are there any equivalents to the Rollup and Cube operators in Access? If not, how can I write subtotal and grand total (summary data) queries in Access?
I have written :
SELECT a,b FROM dumy
UNION ALL select a,sum(b) from dumy
group by a ;
The output is like the image below, or something similar to that,
But the expected result is this:

You can use Crosstab queries. This will produce one row:
TRANSFORM Count(b.ID) AS [Count]
SELECT "BASE" AS SomeName, Count(b.ID) AS Total
FROM ATable AS b
WHERE Something='Text'
GROUP BY "BASE"
PIVOT AFieldName;

It just requires sorting.
Select T.Total
, T.a
, T.b
From (
SELECT '' as Label
, a
,b
FROM dumy
UNION ALL
select 'Total' as Label
, a
,sum(b)
from dumy
group by a
) as T
Order By T.a, T.Total;

Related

Turn quarterly data into monthly by repeating the quarterly rows by 3

I'm wondering how to repeat each of these rows 3 times to get them from Quarters into months.
I need to repeat the same values in the first 2 columns but depending on the quarter in the third column I would need the other months in that quarter, i.e for the first row '31/01/2021' and '28/02/2021'
So desired output would look like:
Another option is via a CROSS APPLY
Select A.Code
,A.Value
,B.Date
From YourTable A
Cross Apply ( values (EOMonth(dateadd(MONTH,-2,A.Date)))
,(EOMonth(dateadd(MONTH,-1,A.Date)))
,(EOMonth(dateadd(MONTH,-0,A.Date)))
) B(Date)
Results
WITH TABLE_DATA(CODE,VAL,DATED)AS
(
SELECT 'R01',777,'2021-03-31' UNION ALL
SELECT 'R01',833,'2021-06-30' UNION ALL
SELECT 'R01',882,'2021-09-30'
)
SELECT D.CODE,D.VAL,CAST(DATEADD(MONTH,-X.PLACEHOLDER,D.DATED)AS DATE)AS DATED,X.PLACEHOLDER
FROM TABLE_DATA AS D
CROSS JOIN
(
SELECT 0 AS PLACEHOLDER
UNION ALL
SELECT 1
UNION ALL
SELECT 2
)X
ORDER BY D.CODE,DATED;
Could you please check if this query is suitable for you. TABLE_DATA is an example of data you have provided

SQL syntax for complex GROUP BY with OVER statement: calculating Gini coefficient for multiple sets

I want to calculate the Gini coefficient for a number of sets, containing in a two-column table (here called #cits) containing a value and a set-ID. I have been experimenting with different Gini-coefficient calculations, described here (StackExchange query) and here (StackOverflow question with some good replies). Both of the examples only calculate one coefficient for one table, whereas I would like to do it with a GROUP BY clause.
The #cits table contains two columns, c and cid, being the value and set-ID respectively.
Here is my current try (incomplete):
select count(c) as numC,
sum(c) as totalC,
(select row_number() over(order by c asc, cid) id, c from #cits) as a
from #cits group by cid
selecting numC and totalC works well, of course, but the next line is giving me a headache. I can see that the syntax is wrong, but I can't figure out how to assign the row_number() per c per cid.
EDIT:
Based on the suggestions, I used partition, like so:
select cid,sumC = sum(a.id * a.c)
into #srep
from (
select cid,row_number() over (partition by cid order by c asc) id,
c
from #cits
) as a
group by a.cluster_id1
select count(c) as numC,
sum(c) as totalC, b.sumC
into #gtmp
from #cits a
join #srep b
on a.cid = b.cid
group by a.cid,b.sumC
select
gini = 2 * sumC / (totalC * numC) - (numC - 1) / numC
from #gtmp
This almost works. I get a result, but it is >1, which is unexpected, as the Gini-coefficient should be between 0 and 1. As stated in the comments, I would have preferred a one-query solution as well, but it is not a major issue at all.
You can "partition" the data so row numbering would start over for each ID...
but I'm not sure this is what you're after..
I'm assuming you want the CID displayed as you are grouping by it.
select count(c) as numC
, sum(c) as totalC
, row_number() over(partition by cID order by c asc) as a
, cid
from #cits group by cid
Note you don't need the subquery.
Yeah this isn't likely right.
output
NumC TotalC A CID
24 383 1 1
15 232 1 2
If I'm understanding correctly, you need numC and totalC for each C in a cid set, as well as the position of the c inside of that set. This should get you what you need:
select
rn.cid,
rn.c,
row_number() over (partition by rn.cid order by rn.c) as id,
agg.numC,
agg.totalC
from #cits rn
left outer join
(
select
cid,
count(c) as numC,
sum(c) as totalC
from #cits
group by cid
) agg
on rn.cid = agg.cid

T-SQL Multi-layered CTE query with Aggregates

I have a long Common Table Expression (CTE) query which is trying to calculate percent difference between each users' average score and group average score.
I would like for my multi-layered CTE query to filter and reduce bulk of records down to the following table:
UserID Tag UserAvg GroupAvg PercentDifference
1 Cat 72.50 73 -0.68
2 Cat 75.50 73 3.36
3 Cat 75 73 2.70
4 Cat 73.25 73 0.34
5 Cat 52.3333 73 -32.97
6 Cat 86.25 73 16.64
My problem is getting GroupAvg column so that I can perform % Difference calculation.
To illustrate the current approach I am using; here is the summary of my CTE query:
WITH
-- select 1st 3 columns
UserScores AS (select UserID, Tag, Score FROM {multiple-table} WHERE Tag = 'Cat'),
-- add UserAvg column by grouping records
ScoreAverages AS (select UserID, Tag, AVG(Score) AS UserAvg GROUP BY UserID, Tag FROM UserScores),
-- calculate GroupAvg
GroupAverage AS (select AVG(UserAvg) AS GroupAvg FROM ScoreAverages),
-- calculate % difference
PercentDiff AS (select UserID, Tag, UserAvg, 73 AS GroupAvg, (((UserAvg-73)/((UserAvg+73)/2))*100) AS PercentDifference FROM ScoreAverages )
-- do something with results
select * from PercentDiff
Simple enough; right?
Notice that I have hard coded 73 as my GroupAvg value. I am unsure how to construct required sql query that would allow me to go from ScoreAverages to PercentDiff table.
Is it possible to perform SELECT within a SELECT statement? And I am not looking for something of the following:
select * from X where Id in (select Id from Y where Name like '%abc%')
Or I am simply trying to do too much in one go?
Yes, it's called a sub-select:
SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage, Column3
FROM ...
To use the result of the sub-select in another column's calculation, you can either repeat the sub-select:
SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage, (Column3 - (SELECT QUERY THAT GETS GROUP AVERAGE)) AS Column4
FROM ...
Or you can reference it the same as you would any other column in the outer query or a subsequent CTE:
WITH CTE1 AS (SELECT Column1, Column2, (SELECT QUERY THAT GETS GROUP AVERAGE) AS GroupAverage
FROM ...)
, CTE2 AS (SELECT *, Column3-GroupAverage) AS Column4
FROM CTE1
JOIN ...
It is possible, as shown in Tab Alleman's answer, but in your case it's not necessary. Since you already calculate the GroupAvg in the cte chain, you can use it in the final query. and since the GroupAverage only contains one row, you can simply add a CROSS JOIN to it:
;WITH
-- select 1st 3 columns
UserScores AS (
select UserID, Tag, Score
FROM {multiple-table}
WHERE Tag = 'Cat'),
-- add UserAvg column by grouping records
ScoreAverages AS (
select UserID, Tag, AVG(Score) AS UserAvg
FROM UserScores
GROUP BY UserID, Tag),
-- calculate GroupAvg
GroupAverage AS (
select AVG(UserAvg) AS GroupAvg
FROM ScoreAverages),
-- calculate % difference
PercentDiff AS (
select UserID, Tag, UserAvg, GroupAvg,
(((UserAvg-GroupAvg)/((UserAvg+GroupAvg)/2))*100) AS PercentDifference
FROM ScoreAverages
CROSS JOIN GroupAverage)
-- do something with results
select * from PercentDiff
I just thought you could do this with a single cte like so.
;WITH UserAverages AS
(
SELECT UserID,
Tag,
AVG(Score) AS UserAvg,
AVG(AVG(Score)) OVER () AS GroupAvg
FROM {multiple-table}
WHERE Tag = 'Cat'
GROUP BY UserID, Tag
)
SELECT UserID,
Tag,
UserAvg,
GroupAvg,
(((UserAvg-GroupAvg)/((UserAvg+GroupAvg)/2))*100) AS PercentDifference
FROM UserAverages

select statement with "Group by" on specific columns but displaying other columns along with group by columns

I want to get all data based on group by of only encounter,medicationname
column data..
select encounter,medicationname,count(*) as freq,labdate,result
from Medications where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*)>2
I have records like
encounter medicationname freq
8604261 ACC 3
Now based on this data ,I want to get
This is my desired output
encounter medicationname labtime result
8604261 ACC 2015-05-22 18
8604261 ACC 2015-07-23 23
8604261 ACC 2015-09-09 27
You can use COUNT() as a window function, something like this:
;With Counted as (
SELECT encounter,medicationname,labdate,result,
COUNT(*) OVER (PARTITION BY encounter,medicationname) as cnt
from Medications
where (labdate between #admitdate
and DATEDIFF(dd,24,#admitdate))
)
select encounter,medicationname,labdate,result
from Counted
where cnt > 2
I would note that I think DATEDIFF1 is probably wrong also but since I don't have your data, inputs and an actual spec, I've left it as is for now.
1DATEDIFF returns an int, but you're using it in a comparison against a column which is apparently a date. DATEADD would be the more probably desired function here, but as I say, I don't have full information to go on.
If I understand you question correctly what you need is this
;WITH CTE AS
(
select encounter,medicationname,count(*) as freq,labdate,result
from Medications where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*) > 2
)
select encounter,medicationname,labdate,result
from Medications M
INNER JOIN CTE C
ON M.encounter = C.encounter
AND M.medicationname = C.medicationname
where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
or better yet using COUNT()OVER()
;WITH CTE AS
(
SELECT encounter,medicationname,COUNT(*) OVER(PARTITION BY encounter,medicationname)as freq,labdate,result
FROM Medications
WHERE (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
)
SELECT * FROM CTE
WHERE freq > 2
select encounter,medicationname,count(*) as freq,labdate,result
from Medications
where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*) > 2

How to do arithmetic operations on data fields and aggregate functions in where clause?

I'm using this query to return the distribution of a float field around its average:
SELECT COUNT(*) AS [Count], Result FROM (
SELECT ROUND(Result - AVG(Result) OVER(), 1) Result FROM Results)
GROUP BY Result
This query returns the distance between all records and the mean. Now, I need to filter the records which are out of the ±3SD range. I thought perhaps I could easily achieve this by changing my query to this:
SELECT COUNT(*) AS [Count], Result FROM (
SELECT ROUND(Result - AVG(Result) OVER(), 1) Result FROM Results
HAVING Abs(Result - AVG(Result)) OVER() < 3 * STDEV(Result) OVER())
GROUP BY Result
But SQL Server is giving me two errors:
Column 'Results.Result' is invalid in the HAVING clause because it is
not contained in either an aggregate function or the GROUP BY clause.
Windowed functions can only appear in the SELECT or ORDER BY clauses.
How can I achieve what I'm looking for? Google isn't kind to me today :-(
As the second error message indicated, windowed functions can only appear in the SELECT or ORDER BY clauses - so rather than including them in the HAVING clause, include them in the inner SELECT and then select on them in the outer WHERE clause - like so:
SELECT COUNT(*) AS [Count], Result FROM
(SELECT ROUND(Result - AVG(Result) OVER(), 1) Result,
Abs(Result - AVG(Result) OVER()) avgdiff,
STDEV(Result) OVER() stddev
FROM Results) r
WHERE avgdiff < 3 * stddev
GROUP BY Result

Resources