How to create SQL view with Count() - sql

This is my query:
select
Sales.SaleID,
Sales.StartSaleDate,
Sales.EndSaleDate,
Sales.SalePercent,
COUNT(LessonID) as TotalLesson,
Sales.Status,
Sales.ExpiredStatus,
Sales.SalePrice,
Sales.IsSpecial
FROM
Sales
LEFT JOIN
SaleLessons ON SaleLessons.SaleID = Sales.SaleID
GROUP BY
Sales.Status, Sales.IsSpecial, Sales.StartSaleDate, Sales.EndSaleDate,
Sales.SalePercent, Sales.SaleID, Sales.ExpiredStatus, Sales.SalePrice
ORDER BY
Sales.StartSaleDate DESC

create view ViewSchema.ViewName
as
select Sales.SaleID,
Sales.StartSaleDate,
Sales.EndSaleDate,
Sales.SalePercent,
COUNT(LessonID) as TotalLesson,
Sales.Status,
Sales.ExpiredStatus,
Sales.SalePrice,
Sales.IsSpecial
from Sales
LEFT JOIN SaleLessons
ON SaleLessons.SaleID = Sales.SaleID
group by Sales.Status,
Sales.IsSpecial,
Sales.StartSaleDate,
Sales.EndSaleDate,
Sales.SalePercent,
Sales.SaleID,
Sales.ExpiredStatus,
Sales.SalePrice
You really don't need the ORDER BY clause, you can use it later when extracting data from the view.
Also, here is a very informative answer on this subject - https://stackoverflow.com/a/15188437/7119478

Related

SQL add a column with COUNT(*) to my query

I need to add a column with the content of this query :
SELECT COUNT(*) FROM account_subscriptiongroups WHERE account_subscriptiongroups.active = true AND account_subscriptiongroups.user_id = account_user.id
to this query :
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
LEFT JOIN
account_subscriptiongroup ON account_adminaction.sub_group_id = account_subscriptiongroup.id
WHERE
account_adminaction.created_on >= '2021-04-07' AND account_adminaction.created_on <= '2021-04-13' AND
((account_adminaction.description LIKE 'Arrêt de l''abonnement%') OR (account_adminaction.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY
subscription_ended_on
I tried adding a LEFT JOIN like that:
LEFT JOIN
account_subscriptiongroup all_sg ON account_user.id = account_subscriptiongroup.user_id
with this line in my WHERE statement :
AND all_sg.active = true
and this line in my SELECT :
COUNT(all_sg.id)
but I get an error :
ERROR: column "account_user.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 2: account_user.id as user_id, account_user.email, account_us...
^
I don't understand how I could perform this action properly
To count something, you need to specify a group where that count applies.
So every column that you select (and is not used in an aggregate function, like COUNT or SUM), you need to mention in the GROUP BY clause.
Or to put it the other way around: the non-aggregate columns must apply to all rows that are contained in that particular COUNT.
So between the WHERE and ORDER BY clauses, add a GROUP BY clause:
GROUP BY account_user.id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id,
account_adminaction.description,
account_adminaction.id,
account_adminaction.created_on
If, on the other hand, you want a count from a different table, you can add a sub-select:
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on,
(SELECT COUNT(*)
FROM account_subscriptiongroups
WHERE account_subscriptiongroups.active = true
AND account_subscriptiongroups.user_id = account_user.id) AS groupcount
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
You can left join to to a derived table that does the grouping and counting:
SELECT au.id as user_id, au.email, au.first_name, au.last_name, au.phone,
asg.id as sub_group_id,
ad.description,
ad.id as admin_action_id,
ad.created_on as subscription_ended_on,
asgc.num_groups
FROM account_adminaction ad
LEFT JOIN account_user au ON au.id = ad.user_id
LEFT JOIN account_subscriptiongroups asg on ON ad.sub_group_id = asg.id
LEFT JOIN (
SELECT user_id, count(*) as num_groups
FROM account_subscriptiongroups ag
WHERE ag.active
GROUP by user_id
) asgc on asgc.user_id = au.id
WHERE ad.created_on >= '2021-04-07'
AND ad.created_on <= '2021-04-13'
AND ((ad.description LIKE 'Arrêt de l''abonnement%') OR (ad.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY subscription_ended_on
It's not entirely clear to me, what you are trying to count, but another option (most probably slower) could be to use a window function combined with a filter clause:
count(*) filter (where asg.active) over (partition by asg.user_id) as num_groups
EDIT: my answer is the same as submitted by a_horse_with_no_name
Two answers, a literal one just solving the problem you posed, and then another one questioning whether what you asked for is really what you want.
Simple answer: modify your desired query to add user_id to the Select and remove user_id from the Where clause. Now you have a table that can be left-joined to the rest of your larger query.
...
Left Join (Select user_id, count(*) as total_count
From account_subscriptiongroup
Where account_subscriptiongroups.active = true
Group By user_id) Z
On Z.user_id=account_user.id
I question whether this count is what you really want here. This counts every account_subscriptiongroup entry for all time but only the active ones. Your larger query brings back inactive as well as active records, so if your goal is to create a denominator for a percentage, you are mixing 'apples and oranges' here.
If you decide you want a total by user of the records in your query instead, then you can add one more element to your larger query without adding any more tables. Use a windowing function like this:
Select ..., Sum(Case When account_subscriptiongroup.active Then 1 else 0 End) Over (Group By account_user.id) as total count
This just counts the records within the date range and having the desired actions.

Use count and group by in a joins table?

Here is my query and I want to add the "count of SalID group by OFID" and store the result in the same table.
SELECT
T_OF.OFID,
T_OF.OFDateDPrev, T_OF.OFDateFPrev,
T_OF_User.OFUserID,
T_OF_User.SalID
INTO T_tracing
FROM T_OF
INNER JOIN T_OF_User
ON T_OF_User.OFID = T_OF.OFID
I tried this:
SELECT
T_OF.OFID,
T_OF.OFDateDPrev, T_OF.OFDateFPrev,
T_OF_User.OFUserID,
Count (SalID) FROM T_OF_User GROUP BY OFID
INTO T_tracing
FROM T_OF
INNER JOIN T_OF_User
ON T_OF_User.OFID = T_OF.OFID
But I have an error message. Any help please?
I think you want a window function:
SELECT T_OF.OFID, T_OF.OFDateDPrev, T_OF.OFDateFPrev, T_OF_User.OFUserID,
Count(SalID) OVER (PARTITION BY T_OF.OFID) as cnt
INTO T_tracing
FROM T_OF JOIN
T_OF_User
ON T_OF_User.OFID = T_OF.OFID;
You also need to give the result of the expression a name for T_Tracing.

Left Join on subquery

Can someone help me with this query? In the subquery P, a fiscal year exists where the same year does not exist in BudgetActivityDetailCurrentBiennium. I need this query to show a null value for Amount in that year. Currently that year does not show up at all.
SELECT
P.FiscalYear
,P.BudgetNbr
,SUM(sec.BudgetActivityDetailCurrentBiennium.TranAmount) AS Amount
FROM
(SELECT
sec.BudgetIndexCurrentBiennium.BudgetNbr
,AVG(CAST(sec.BudgetIndexCurrentBiennium.BienniumYear AS INT)+1) AS FiscalYear
FROM
sec.BudgetIndexCurrentBiennium
GROUP BY
sec.BudgetIndexCurrentBiennium.BudgetNbr
UNION ALL
SELECT
sec.BudgetIndexCurrentBiennium.BudgetNbr
,AVG(CAST(sec.BudgetIndexCurrentBiennium.BienniumYear AS INT)+2) AS FiscalYear
FROM
sec.BudgetIndexCurrentBiennium
GROUP BY
sec.BudgetIndexCurrentBiennium.BudgetNbr) AS P
LEFT JOIN sec.BudgetActivityDetailCurrentBiennium
ON
P.BudgetNbr = sec.BudgetActivityDetailCurrentBiennium.BudgetNbr
AND P.FiscalYear = sec.BudgetActivityDetailCurrentBiennium.FiscalYear
WHERE sec.BudgetActivityDetailCurrentBiennium.BudgetNbr = '076036'
GROUP BY
P.FiscalYear
,P.BudgetNbr
I think the problem is your WHERE clause,
WHERE sec.BudgetActivityDetailCurrentBiennium.BudgetNbr = '076036'
which applies to the SELECT as a whole, not just (as I guess you intend) to the left join.
Change the WHERE to an AND. That should do the trick.

sql query with group by and count

I have this ms access query:
SELECT weber, t1.artnr, t1.varnr, t1.lfdnr_kal, fert.invnr, fert.min, fert.[lst/h] FROM (
SELECT stammdat.weber, lackzahlen.artnr, lackzahlen.varnr, first(stammdat.[lfdnr-kal]) AS lfdnr_kal
FROM lackzahlen LEFT JOIN stammdat ON (lackzahlen.artnr=cstr(IIf(stammdat.artnr Is Null,'',cstr(stammdat.artnr)))) AND (lackzahlen.varnr=cstr(IIf(stammdat.varnr Is Null,'',cstr(stammdat.varnr))))
GROUP BY lackzahlen.artnr, lackzahlen.varnr, io_stkzahl, weber) AS t1
LEFT JOIN fert ON (t1.lfdnr_kal=fert.[lfdnr-kal]) AND (t1.artnr=cstr(iif(fert.artnr Is Null,'',cstr(fert.artnr)))) WHERE fert.invnr IN ('338902', 'R20000')
Now, I would like to filter the records on the image:
As you see, I would like to have the records, where there is pro weber, artnr, varnr, lfdnr_kal and invnr more than one row with the same min and lst/h value.
I guess, I should use cound (maybe having count) and group by. But how?
In the example, I haven't marked all the rows.
Add group by and having to get the duplicate rows
GROUP BY weber, t1.artnr, t1.varnr, t1.lfdnr_kal, fert.invnr, fert.min, fert.[lst/h]
having count(*) >1
Try below SQL : As you didnt mention sum or AVG values i guess distinct records will solve your problem.
select weber, a.artnr,a.varnr, a.lfdnr_kal, a.invnr, a.min, a.lst_h
(
SELECT weber, t1.artnr, t1.varnr, t1.lfdnr_kal, fert.invnr, fert.min, fert.[lst/h] as lst_h FROM (
SELECT stammdat.weber, lackzahlen.artnr, lackzahlen.varnr, first(stammdat.[lfdnr-kal]) AS lfdnr_kal
FROM lackzahlen LEFT JOIN stammdat ON (lackzahlen.artnr=cstr(IIf(stammdat.artnr Is Null,'',cstr(stammdat.artnr)))) AND (lackzahlen.varnr=cstr(IIf(stammdat.varnr Is Null,'',cstr(stammdat.varnr))))
GROUP BY lackzahlen.artnr, lackzahlen.varnr, io_stkzahl, weber) AS t1
LEFT JOIN fert ON (t1.lfdnr_kal=fert.[lfdnr-kal]) AND (t1.artnr=cstr(iif(fert.artnr Is Null,'',cstr(fert.artnr)))) WHERE fert.invnr IN ('338902', 'R20000')
) a group by weber, a.artnr,a.varnr, a.lfdnr_kal, a.invnr, a.min, a.lst_h
Having count(*) > 1
Hope this helps :)

SQL - Derived tables issue

I have the following SQL query:
SELECT VehicleRegistrations.ID, VehicleRegistrations.VehicleReg,
VehicleRegistrations.Phone, VehicleType.VehicleTypeDescription,
dt.ID AS 'CostID', dt.IVehHire, dt.FixedCostPerYear, dt.VehicleParts,
dt.MaintenancePerMile, dt.DateEffective
FROM VehicleRegistrations
INNER JOIN VehicleType ON VehicleRegistrations.VehicleType = VehicleType.ID
LEFT OUTER JOIN (SELECT TOP (1) ID, VehicleRegID, DateEffective, IVehHire,
FixedCostPerYear, VehicleParts, MaintenancePerMile
FROM VehicleFixedCosts
WHERE (DateEffective <= GETDATE())
ORDER BY DateEffective DESC) AS dt
ON dt.VehicleRegID = VehicleRegistrations.ID
What I basically want to do is always select the top 1 record from the 'VehicleFixedCosts' table, where the VehicleRegID matches the one in the main query. What is happening here is that it's selecting the top row before the join, so if the vehicle registration of the top row doesn't match the one we're joining to it returns nothing.
Any ideas? I really don't want to have use subselects for each of the columns I need to return
Try this:
SELECT vr.ID, vr.VehicleReg,
vr.Phone, VehicleType.VehicleTypeDescription,
dt.ID AS 'CostID', dt.IVehHire, dt.FixedCostPerYear, dt.VehicleParts,
dt.MaintenancePerMile, dt.DateEffective
FROM VehicleRegistrations vr
INNER JOIN VehicleType ON vr.VehicleType = VehicleType.ID
LEFT OUTER JOIN (
SELECT ID, VehicleRegID, DateEffective, IVehHire, FixedCostPerYear, VehicleParts, MaintenancePerMile
FROM VehicleFixedCosts vfc
JOIN (
select VehicleRegID, max(DateEffective) as DateEffective
from VehicleFixedCosts
where DateEffective <= getdate()
group by VehicleRegID
) t ON vfc.VehicleRegID = t.VehicleRegID and vfc.DateEffective = t.DateEffective
) AS dt
ON dt.VehicleRegID = vr.ID
Subquery underneath dt might need some grouping but without schema (and maybe sample data) it's hard to say which column should be involved in that.