I'm using SQL Server. I have a nice summary table like the one you see below. I want to populate (create) the Pct field for each proficiency level.
| MeasurementScale | Grade | ProficiencyLevel | PL_Count | Pct |
|------------------|-------|------------------|----------|-----|
| Mathematics | 6 | Did Not Meet | 40 | |
| Mathematics | 6 | Approaches | 86 | |
| Mathematics | 6 | Meets | 83 | |
| Mathematics | 6 | Masters | 42 | |
| Mathematics | 6 | Total | 251 | |
I basically want something like the following query, I just don't know how to write it.
SELECT SchoolName
,MeasurementScale
,Grade
,ProficiencyLevel
,PL_Count
,(PL_Count / (SELECT PL_Count FROM #PL_Summary1920 WHERE ProficiencyLevel = 'Total')) as Pct
FROM #PL_Summary1920
GROUP BY SchoolName
,MeasurementScale
,Grade
,ProficiencyLevel
,PL_Count
SELECT V1.*,
CASE WHEN V2.PL_COUNT = 0 THEN 0
ELSE V1.PL_Count * 1.0/ V2.PL_COUNT
END AS PCT
FROM (
SELECT SchoolName,
MeasurementScale,
Grade,
ProficiencyLevel,
SUM(PL_Count) AS PL_Count
FROM #PL_Summary1920 T1
GROUP BY SchoolName
,MeasurementScale
,Grade
,ProficiencyLevel
) V1
LEFT JOIN (
SELECT SchoolName,
MeasurementScale,
Grade,
SUM(TT.PL_Count) AS PL_COUNT
FROM #PL_Summary1920 TT
WHERE TT.ProficiencyLevel = 'Total'
GROUP BY SchoolName,
MeasurementScale,
Grade
) V2 ON V2.SchoolName = V1.SchoolName
AND V2.MeasurementScale = V1.MeasurementScale
AND V2.Grade = V1.Grade
Try this:
SELECT MeasurementScale
,Grade
,ProficiencyLevel
,PL_Count
,PL_Count * 1.0 / (SELECT PL_Count FROM #PL_Summary1920 WHERE ProficiencyLevel = 'Total') as Pct
FROM #PL_Summary1920
GROUP BY SchoolName
,MeasurementScale
,Grade
,ProficiencyLevel
,PL_Count
Doing the multiplication by 1.0 forces a type conversion to decimal, so your percentages display correctly. It's cleaner than doing a CAST or CONVERT.
Related
Let's say we have this and want to see all Tasks, that havent been done yet and an additional column showing how many open Tasks there are left for this customer.
I have a table like this in my database:
+------------+--------------------------+-------+
| CustomerID | Task | Done |
+------------+--------------------------+-------+
| 1 | CleanRoom | False |
| 1 | Cleandishes | True |
| 1 | WashClothes | False |
| 2 | TakeDogsOut | True |
| 2 | PlayWithKids | True |
| 3 | HaveFunWithMrSamplesWife | True |
| 3 | CleanMrSamplesCar | False |
+------------+--------------------------+-------+
I need this as returned table:
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 2 |
| 1 | WashClothes | 2 |
| 3 | CleanMrSamplesCar | 1 |
+------------+-------------------+-------------+
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I know I could go like
SELECT
a.CustomerID,
a.Task,
(
Select count(*) from myTable where
customerID = a.CustomerID and
done = False
) as DoneOverAll
FROM myTable as a
WHERE Done = False
But I think that this is very ineffective, since it would execute a Select Count for each row in my table. Is there a way to achieve this with a JOIN using groupBy or something? I'm not into GroupBy commands yet.
Okay I should have tried first. Came up with the following;
Select count(*), CustomerID from myTable group by CustomerID
All I need to do now is to get this into a join.
Okay, got it. Sorry again for not trying first!
SELECT
a.CustomerID,
a.Task,
b.cnt
FROM myTable as a
LEFT JOIN (select count(*) AS cnt, CustomerID FROM myTable GROUP BY CustomerID) as b on a.CustomerID = B.CustomerID
WHERE Done = False
Question left;
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I'm not sure why Done = False, but this is your logic. :-)
Here's what I would do, without the LEFT JOIN.
SELECT
a.CustomerID,
a.Task,
SUM(CASE WHEN a.Done = 'False' THEN 1 ELSE 0 END) DoneOverAll,
SUM(Case WHEN a.Done = 'True' THEN 1 ELSE 0 END) NotDone
FROM myTable as a
Group By a.CustomerID, a.Task
Do calculate separately .
;with tempfalse as(
SELECT
a.CustomerID,
a.Task,
count(*) as DoneOverAll
FROM myTable as a
WHERE Done = False
group by a.CustomerID, a.Task
)
, temptrue (
SELECT
a.CustomerID,
a.Task,
count(*) as total
FROM myTable as a
group by a.CustomerID, a.Task
)
SELECT
a.CustomerID,
a.Task,
cast(NULLIF(DoneOverAll,0) as varchar (10) ) + '/' + cast(NULLIF(b.total,0) as varchar (10) )
from temptrue as a left join tempfalse b
on a.CustomerID =a.CustomerID and
a.Task = b.Task
I'm attempting to pivot some of my rows, currently my query is this:
SELECT count(distinct users.userName) as TotalUsers, products.productNameCommon, employees.Division, products.productType
FROM FlexLM_users users
INNER JOIN Org.Employees employees ON employees.Username=users.userName
INNER JOIN FlexLM_history history ON users.userID=history.userID
INNER JOIN FlexLM_products products ON products.productID=history.productID
where products.productType = 'Base'
GROUP BY products.productNameCommon, employees.Division, products.productType
ORDER BY users DESC
And outputs this:
TotalUsers| productNameCommon | Division | productType
------------------------------------------------------------------------
16 | Standard | Disease Control | base
12 | Basic | Epidemiology | base
10 | Standard | Prevention | base
8 | Advanced | Epidemiology | base
6 | Basic | Disease Control | base
2 | Advanced | Prevention | base
What I am looking to do is this:
Division | Basic | Standard | Advanced | TotalUsers
----------------------------------------------------------
Disease Control| 6 | 16 | 0 | 22
Epidemiology | 12 | 0 | 8 | 20
Prevention | 0 | 10 | 2 | 12
SELECT Division
, ISNULL([Basic] , 0) AS [Basic]
, ISNULL([Standard], 0) AS [Standard]
, ISNULL([Advanced], 0) AS [Advanced]
, ISNULL([Basic] , 0)
+ ISNULL([Standard], 0)
+ ISNULL([Advanced], 0) AS TotalUsers
FROM (
SELECT TotalUsers , productNameCommon , Division
FROM (
-- Your Existing Query here
)a
) t
PIVOT (
SUM (TotalUsers)
FOR productNameCommon
IN ([Basic], [Standard] , [Advanced])
) p
I want to group different colons in same range by row. Example:
Amount1 | Amount2
------------------------
20,00 | 30,00
35,00 | 32,00
12,00 | 51,00
101,00 | 100,00
Here result should be;
Range |TotalAmount1 |TotalAmount2 | CountAmount1 | CountAmount2 | RateOfCountAmount1
-----------------------------------------------------------------------------
0-50 | 67,00 | 62,00 | 3 | 1 | %75
50-100 | 0,00 | 151,00 | 0 | 2 | %0
100+ | 101,00 | 0,00 | 1 | 0 | %25
Total | 168,00 | 213,00 | 4 | 3 | %100
Here is the example : http://sqlfiddle.com/#!9/05fd3
you can query like this
;with cte as (
select case when amount1 < 50 then '0-50'
when amount1 between 50.01 and 100 then '50-100'
when amount1 > 100 then '100+' end as rngamt1,
case when amount2 < 50 then '0-50'
when amount2 between 5.01 and 100 then '50-100'
when amount2 > 100 then '100+' end as rngamt2,
* from amounts
), cte2 as (select coalesce(rngamt1, rngamt2) as [Range], isnull(a.TotalAmount1,0) as TotalAmount1, isnull(b.TotalAmount2, 0) as TotalAmount2, isnull( a.TotalCount1 , 0) as TotalCount1, isnull(b.TotalCount2, 0) as Totalcount2 from
(select rngamt1, sum(amount1) TotalAmount1, count(amount1) TotalCount1 from cte c
group by rngamt1) a
full join
(select rngamt2, sum(amount2) TotalAmount2, count(amount2) TotalCount2 from cte c
group by rngamt2) b
on a.rngamt1 = b.rngamt2
)
select *, (TotalCount1 * 100 )/sum(TotalCount1) over () as RateCount1
from cte2
union
select 'Total' as [Range], sum(TotalAmount1) as TotalAmount1, sum(totalAmount2) as TotalAmount2,
sum(TotalCount1) as TotalCount1, sum(Totalcount2) as TotalCount2, (sum(TotalCount1)*100)/Sum(TotalCount1) as RateCount1 from cte2
I have following data:
+----------------+--------------+-----+
| StgDescription | ID | Amt |
+----------------+--------------+-----+
| A | OA17 | 11 |
| A | OA17 | 11 |
| A | OA17 | 11 |
| A | OA17 | 11 |
| B | ZA47/ A | 12 |
| B | ZA47/ A | 12 |
| B | ZA47/ B | 10 |
| B | ZA47/ B | 10 |
| B | ZA48/ A | 14 |
| B | ZA48/ F | 10 |
| B | ZA48 /G | 13 |
| B | ZA48 /H | 10 |
| B | ZA48/ I | 15 |
| B | ZA48/ J | 10 |
| B | ZA48/ K | 16 |
| B | ZA48/ L | 10 |
| c | FA01LM100340 | 10 |
| c | PA53 AE | 10 |
+----------------+--------------+-----+
I want to generate report in following format. The amount should be sum for ID for same StgDescription.
+----------------+-----+
| StgDescription | Amt |
+----------------+-----+
| a | 11 |
| b | 120 |
| c | 20 |
+----------------+-----+
I've written following query to get this result:
WITH CTE AS(
SELECT
distinct
s.StgDescription
,p.ID
,Amt
FROM [DinDb].[dbo].[tblTvlTransaction] t
JOIN tblstgmaster s on t.StgId=s.StgId
JOIN tblProjDocSt p on t.TDocID=p.DocId
JOIN [PdasDb].[dbo].[tblIDmaster] f ON p.ID=f.ID
where OptAuthoDateTime between '2015-07-27 00:00:00' and '2015-09-01 00:00:00')
select StgDescription,sum(AMT) from cte group by StgDescription
Is there any other efficient alternative to do this?
First in cte remove duplicates, then GROUP BY like:
WITH cte AS (
SELECT DISTINCT StgDescription, ID, Amt
FROM your_tab
)
SELECT
StgDescription,
Amt = SUM(Amt)
FROM cte
GROUP BY StgDescription;
OR:
WITH cte AS (
SELECT StgDescription, ID, Amt
FROM your_tab
GROUP BY StgDescription, ID, Amt
)
SELECT
StgDescription,
Amt = SUM(Amt)
FROM cte
GROUP BY StgDescription;
I hope that you get the data from a query, not from a table. It would not be good to store data thus redundantly. And it would not be gould to name a column ID which is not the unique identifier for a row in a table.
Your problem with the data is that you have duplicates, which prevents you from getting the sum directly. So use DISTINCT to make your data unique first.
If this data is from a query then simply add DISTINCT after the SELECT keyword. If not, use a derived table (i.e. a subquery) where you select distinct records from the table.
select stgdescription, sum(amt)
from
(
select distinct stgdescription, id, amt
from mydata
) distinct_data
group by stgdescription;
You may want to replace stgdescription with lower(stgdescription), though, if stgdescription can be 'A' or 'a' and you want to treat them the same.
I'd keep it as simple as possible, like this:
select StgDescription, sum(Amt) from
(
select distinct StgDescription, ID, Amt from tablename
) a
group by StgDescription
Hope it helps!
I suspect your duplicates are coming from [tblTvlTransaction], therefore, I would remove this table as a JOIN and use EXISTS to just check a record is there. So essentially the only tables in the FROM clause are those you actually need data from:
SELECT s.StgDescription, p.ID, s.Amt
FROM tblstgmaster AS s
INNER JOIN tblProjDocSt p on
t.TDocID = p.DocId
INNER JOIN [PdasDb].[dbo].[tblIDmaster] AS f
ON p.ID = f.ID
WHERE EXISTS
( SELECT 1
FROM [DinDb].[dbo].[tblTvlTransaction] AS t
WHERE t.OptAuthoDateTime BETWEEN '2015-07-27 00:00:00' AND '2015-09-01 00:00:00'
AND t.StgId = s.StgId
);
The advantage of EXISTS is that it can use a semi-join, which essentially means rather than pulling back all the rows from the transaction table, it will stop the seek/scan as soon as it finds one matching record. This should leave you without duplicates so you can do the SUM directly:
SELECT s.StgDescription, Amount = SUM(s.Amt)
FROM tblstgmaster AS s
INNER JOIN tblProjDocSt p on
t.TDocID = p.DocId
INNER JOIN [PdasDb].[dbo].[tblIDmaster] AS f
ON p.ID = f.ID
WHERE EXISTS
( SELECT 1
FROM [DinDb].[dbo].[tblTvlTransaction] AS t
WHERE t.OptAuthoDateTime BETWEEN '2015-07-27 00:00:00' AND '2015-09-01 00:00:00'
AND t.StgId = s.StgId
)
GROUP BY s.StgDescription;
Hello I have a temp table (#tempResult) that contains results like the following...
-----------------------------------------
| DrugAliasID | Dosage1 | Unit1 | rowID |
-----------------------------------------
| 322 | 10 | MG | 1 |
| 322 | 50 | ML | 2 |
| 441 | 20 | ML | 3 |
| 443 | 15 | ML | 4 |
-----------------------------------------
I'm looking to get the results to be like the following, pivoting the rows that have the same DrugAliasID.
--------------------------------------------------
| DrugAliasID | Dosage1 | Unit1 | Dosage2 | Unit2 |
--------------------------------------------------
| 322 | 10 | MG | 50 | ML |
| 441 | 20 | ML | NULL | NULL |
| 443 | 15 | ML | NULL | NULL |
--------------------------------------------------
So far I have a solution that isn't using pivot. I'm not too good with pivot and was wondering if anyone knew how to use it in this scenario. Or solve it some other way. Thanks
SELECT
tr.drugAliasID,
MIN(trmin.dosage1) AS dosage1,
MIN(trmin.unit1) AS unit1,
MIN(trmax.dosage1) AS dosage2,
MIN(trmax.unit1) AS unit2
FROM
#tempResult tr
JOIN
#tempResult trmin ON trmin.RowID = tr.rowid AND trmin.drugAliasID = tr.drugAliasID
JOIN
#tempResult trmax ON trmax.RowID = tr.rowid AND trmax.drugAliasID = tr.drugAliasID
JOIN
(SELECT
MIN(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr1 ON tr1.rowid = trmin.RowID
JOIN
(SELECT
MAX(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr2 ON tr2.rowid = tr.RowID
GROUP BY
tr.drugAliasID
HAVING
count(tr.drugAliasID) > 1
Assuming your version of SQL Server supports the use of CTEs, you can simplify your query thus:
;with cte as
(select *, row_number() over (partition by drugaliasid order by rowid) rn
from #tempResult
)
select c.drugaliasid, c.dosage1, c.unit1, c2.dosage1 as dosage2, c2.unit1 as unit2
from cte c
left join cte c2 on c.drugaliasid = c2.drugaliasid and c.rn = 1 and c2.rn = 2
where c.rn = 1
Demo
This will give you the desired result, without having to use the pivot keyword.