Getting error on nested query - sql

I'm trying to develop a time sheet page on my web site, and this is my database stracture:
I want to get total hours(TimeSheetWeeks table's sum(timeFrom-timeTo)), total expense(TimeSheetWeeks's table's total amount) and total(TimSheetWeeks table's total amount+ compAllow table's total Amount).
This is my query I wrote to get my result:
;WITH w(tot, tid, eid, fd, td, am, mw) AS
(
SELECT Total = tsw.amount+ca.amount , tsw.[TimeSheetID], [EmployeeID],
[FromDate],[ToDate], tsw.[Amount], SUM(DATEDIFF(MINUTE, [timeFrom],[timeTo] ))
FROM
TimeSheet ts
INNER JOIN (
SELECT SUM(amount) amount, TimeSheetID
FROM TimeSheetWeeks
GROUP BY TimeSheetID
) tsw ON ts.TimeSheetID = tsw.TimeSheetID INNER JOIN (
SELECT SUM(amount) amount, TimeSheetID
FROM CompAllow
GROUP BY TimeSheetID
) ca ON ts.TimeSheetID = ca.TimeSheetID INNER JOIN (
SELECT timeFrom, timeTo, TimeSheetID
FROM TimeSheetWeeks
) AS tss ON tss.TimeSheetID=ts.TimeSheetID
WHERE ts.TimeSheetID=6
Group By tsw.[TimeSheetID], [EmployeeID], [FromDate], [ToDate], tsw.[Amount]
)
SELECT tot, tid, eid, fd, td, Amount = am, totalHrs = RTRIM(mw/60) + ':' +
RIGHT('0'+ RTRIM(mw%60),2)
FROM w;
this quer causes an error saying
Column 'ca.amount' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
What goes wrong here? thanx in advance.

The ca.amount column is referenced in the w CTE, but is neither aggregated nor included in the GROUP BY clause:
;WITH w(tot, tid, eid, fd, td, am, mw) AS
(
SELECT Total = tsw.amount+ca.amount , tsw.[TimeSheetID], [EmployeeID],
[FromDate],[ToDate], tsw.[Amount], SUM(DATEDIFF(MINUTE, [timeFrom],[timeTo] ))
FROM
TimeSheet ts
INNER JOIN (
SELECT SUM(amount) amount, TimeSheetID
FROM TimeSheetWeeks
GROUP BY TimeSheetID
) tsw ON ts.TimeSheetID = tsw.TimeSheetID INNER JOIN (
SELECT SUM(amount) amount, TimeSheetID
FROM CompAllow
GROUP BY TimeSheetID
) ca ON ts.TimeSheetID = ca.TimeSheetID INNER JOIN (
SELECT timeFrom, timeTo, TimeSheetID
FROM TimeSheetWeeks
) AS tss ON tss.TimeSheetID=ts.TimeSheetID
WHERE ts.TimeSheetID=6
Group By tsw.[TimeSheetID], [EmployeeID], [FromDate], [ToDate], tsw.[Amount]
)
SELECT tot, tid, eid, fd, td, Amount = am, totalHrs = RTRIM(mw/60) + ':' +
RIGHT('0'+ RTRIM(mw%60),2)
FROM w;
Either add it to GROUP BY or change the Total expression like this:
Total = tsw.amount + SUM(ca.amount)
depending upon what is the business rule for this query.

The error states the problem:
SELECT SUM(amount) amount, TimeSheetID
FROM CompAllow
GROUP BY TimeSheetID
) ca ...
I'm not 100% sure, but my initial recommendation is to drop the "group by" in this clause. At best, it's redundant.
I'd also recommend trying each of the subclauses separately - make sure they're syntactically correct, make sure they're returning expected results.
IMHO...

Related

SQLite Getting multiple results with LIMIT 1

I have the following problem.
Part of a task is to determine the visitor(s) with the most money spent between 2000 and 2020.
It just looks like this.
SELECT UserEMail FROM Visitor
JOIN Ticket ON Visitor.UserEMail = Ticket.VisitorUserEMail
where Ticket.Date> date('2000-01-01') AND Ticket.Date < date ('2020-12-31')
Group by Ticket.VisitorUserEMail
order by SUM(Price) DESC;
Is it possible to output more than one person if both have spent the same amount?
Use rank():
SELECT VisitorUserEMail
FROM (SELECT VisitorUserEMail, SUM(PRICE) as sum_price,
RANK() OVER (ORDER BY SUM(Price) DESC) as seqnum
FROM Ticket t
WHERE t.Date >= date('2000-01-01') AND Ticket.Date <= date('2021-01-01')
GROUP BY t.VisitorUserEMail
) t
WHERE seqnum = 1;
Note: You don't need the JOIN, assuming that ticket buyers are actually visitors. If that assumption is not true, then use the JOIN.
Use a CTE that returns all the total prices for each email and with NOT EXISTS select the rows with the top total price:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT c.VisitorUserEMail
FROM cte c
WHERE NOT EXISTS (
SELECT 1 FROM cte
WHERE SumPrice > c.SumPrice
)
or:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT VisitorUserEMail
FROM cte
WHERE SumPrice = (SELECT MAX(SumPrice) FROM cte)
Note that you don't need the function date() because the result of date('2000-01-01') is '2000-01-01'.
Also I think that the conditions in the WHERE clause should include the =, right?

Incremental count of duplicates

The following query displays duplicates in a table with the qty alias showing the total count, eg if there are five duplicates then all five will have the same qty = 5.
select s.*, t.*
from [Migrate].[dbo].[Table1] s
join (
select [date] as d1, [product] as h1, count(*) as qty
from [Migrate].[dbo].[Table1]
group by [date], [product]
having count(*) > 1
) t on s.[date] = t.[d1] and s.[product] = t.[h1]
ORDER BY s.[product], s.[date], s.[id]
Is it possible to amend the count(*) as qty to show an incremental count so that five duplicates would display 1,2,3,4,5?
The answer to your question is row_number(). How you use it is rather unclear, because you provide no guidance, such as sample data or desired results. Hence this answer is rather general:
select s.*, t.*,
row_number() over (partition by s.product order by s.date) as seqnum
from [Migrate].[dbo].[Table1] s join
(select [date] as d1, [product] as h1, count(*) as qty
from [Migrate].[dbo].[Table1]
group by [date], [product]
having count(*) > 1
) t
on s.[date] = t.[d1] and s.[product] = t.[h1]
order by s.[product], s.[date], s.[id];
The speculation is that the duplicates are by product. This enumerates them by date. Some combination of the partition by and group by is almost certainly what you need.

Join issue on SQL request

My database :
TB_DW_VAB_FLIGHT : ID_TEC_FLIGHT
TB_DW_VAB_SALES : QUANTITY, TRANSACTION_NUMBER, UNIT_SALES_PRICE
I want to have a table with 4 columns as result : CA, QTE, NB_TRANSACTION and NB_VOLS at the same month. ( N-1 )
I tried a SQL request like this :
SELECT
sum(QUANTITY*UNIT_SALES_PRICE) as CA,
sum(QUANTITY) as QTE,
count(distinct TRANSACTION_NUMBER) as NB_TRANSACTION,
count(distinct ID_TEC_FLIGHT) as NB_VOLS
FROM TB_DW_VAB_SALES, TB_DW_VAB_FLIGHT
where to_char(TB_DW_VAB_SALES.FLIGHT_DATE,'MM')=to_char(current_date,'MM')-1 and to_char(TB_DW_VAB_SALES.FLIGHT_DATE,'YYYY')=to_char(current_date,'YYYY') and SALES_TYPE='SALES'
and to_char(TB_DW_VAB_FLIGHT.FLIGHT_DATE,'MM')=to_char(current_date,'MM')-1 and to_char(TB_DW_VAB_FLIGHT.FLIGHT_DATE,'YYYY')=to_char(current_date,'YYYY');
But Oracle can't give me an answer.
Thank you a lot for any help.
Try
with CTE1 as
(
select to_char(FLIGHT_DATE, 'MM-YYYY') as PERIOD,
sum(QUANTITY*UNIT_SALES_PRICE) as CA,
sum(QUANTITY) as QTE,
count(distinct TRANSACTION_NUMBER) as NB_TRANSACTION
from TB_DW_VAB_SALES
where SALES_TYPE = 'SALES'
group by to_char(FLIGHT_DATE, 'MM-YYYY')
)
, CTE2 as
(
select count(distinct ID_TEC_FLIGHT) as NB_VOLS,
to_char(FLIGHT_DATE, 'MM-YYYY') as PERIOD
from TB_DW_VAB_FLIGHT
group by to_char(FLIGHT_DATE, 'MM-YYYY')
)
select CTE1.CA,
CTE1.QTE,
CTE1.NB_TRANSACTION,
CTE2.NB_VOLS
from CTE1
inner join CTE2 on CTE1.PERIOD = CTE2.PERIOD
where CTE1.PERIOD = to_char(add_Months(sysdate,-1),'MM-YYYY')
or if CTE's are not available in your software:
select CTE1.CA,
CTE1.QTE,
CTE1.NB_TRANSACTION,
CTE2.NB_VOLS
from
(
select to_char(FLIGHT_DATE, 'MM-YYYY') as PERIOD,
sum(QUANTITY*UNIT_SALES_PRICE) as CA,
sum(QUANTITY) as QTE,
count(distinct TRANSACTION_NUMBER) as NB_TRANSACTION
from TB_DW_VAB_SALES
where SALES_TYPE = 'SALES'
group by to_char(FLIGHT_DATE, 'MM-YYYY')
) CTE1
inner join
(
select count(distinct ID_TEC_FLIGHT) as NB_VOLS,
to_char(FLIGHT_DATE, 'MM-YYYY') as PERIOD
from TB_DW_VAB_FLIGHT
group by to_char(FLIGHT_DATE, 'MM-YYYY')
) CTE2
on CTE1.PERIOD = CTE2.PERIOD
where CTE1.PERIOD = to_char(add_Months(sysdate,-1),'MM-YYYY')

How do I get the value associated with a MIN or MAX

I'm in the middle of creating a query and have it where I need the other values, however I am pulling a MIN and MAX date for individual patient_id. I'm wondering how I would go about how I would pull a value associated with that MIN or MAX date as well? I'm looking for a value the column provider_id which will show which doctor they saw on that MIN or MAX date. Here is what I have so far:
WITH test AS (
SELECT patient_id,
clinic,
SUM(amount) AS production,
MIN(tran_date) AS first_visit,
MAX(tran_date) AS last_visit
FROM transactions
WHERE impacts='P'
GROUP BY patient_id, clinic)
SELECT w.patient_id,
w.clinic,
p.city,
p.state,
p.zipcode,
p.sex,
w.production,
w.first_visit,
w.last_visit
FROM test w
LEFT JOIN patient p
ON (w.patient_id=p.patient_id AND w.clinic=p.clinic)
I believe that this will get what you're looking for:
;WITH CTE_Transactions AS (
SELECT DISTINCT
patient_id,
clinic,
SUM(amount) OVER (PARTITION BY patient_id, clinic) AS production,
FIRST_VALUE(tran_date) OVER (PARTITION BY patient_id, clinic ORDER BY tran_date) AS first_visit,
FIRST_VALUE(provider_id) OVER (PARTITION BY patient_id, clinic ORDER BY tran_date) AS first_provider_id,
LAST_VALUE(tran_date) OVER (PARTITION BY patient_id, clinic ORDER BY tran_date) AS last_visit,
LAST_VALUE(provider_id) OVER (PARTITION BY patient_id, clinic ORDER BY tran_date) AS last_provider_id,
ROW_NUMBER() OVER (PARTITION BY patient_id, clinic ORDER BY tran_date) AS row_num
FROM Transactions
WHERE impacts='P'
)
SELECT
w.patient_id,
w.clinic,
p.city,
p.state,
p.zipcode,
p.sex,
w.production,
w.first_visit,
w.last_visit
FROM
CTE_Transactions W
LEFT JOIN Patient P ON
W.patient_id = P.patient_id AND
W.clinic = P.clinic
INNER JOIN Provider FIRST_PROV ON
FIRST_PROV.provider_id = W.first_provider_id
INNER JOIN Provider LAST_PROV ON
LAST_PROV.provider_id = W.last_provider_id
WHERE
W.row_num = 1
I assume you are referring to the CTE. You can use conditional aggregation along with window functions. For instance, to get the amount for the first visit:
WITH test AS (
SELECT patient_id, clinic,
SUM(amount) AS production,
MIN(tran_date) AS first_visit,
MAX(tran_date) AS last_visit,
SUM(CASE WHEN tran_date = min_tran_date THEN amount END) as first_amount
FROM (SELECT t.*,
MIN(trans_date) OVER (PARTITION BY patient_id, clinic) as min_tran_date
FROM transactions
WHERE impacts = 'P'
) t
GROUP BY patient_id, clinic
)

SQL - Replace repeated rows with null values while preserving number of rows

I am trying to get only one instance of a year instead of 12 because I am using this column in a lookup table to provide parameters to a report. Because I am using both monthly and yearly data, I am trying to get them both in the same table.
I have a table like this
--Date--------Year
--------------------
1/2012-------2012
2/2012-------2012
3/2012-------2012
4/2012-------2012
5/2012-------2012
6/2012-------2012
7/2012-------2012
8/2012-------2012
9/2012-------2012
10/2012------2012
11/2012------2012
12/2012------2012
1/2013-------2013
2/2013-------2013
And this is my desired table
--Date--------Year
--------------------
1/2012-------2012
2/2012-------null
3/2012-------null
4/2012-------null
5/2012-------null
6/2012-------null
7/2012-------null
8/2012-------null
9/2012-------null
10/2012------null
11/2012------null
12/2012------null
1/2013-------2013
2/2013-------null
Can someone give me an idea of how to solve a problem like this?
The code I am using right now is
SELECT CAST(MONTH(rmp.EcoDate) AS Varchar(2)) + '/' + CAST(YEAR(rmp.EcoDate) AS varchar(4)) AS Date, Year(rmp.EcoDate) as EcoYear
FROM PhdRpt.ReportCaseList_542 AS rcl INNER JOIN
CaseCases AS cc ON rcl.CaseCaseId = cc.CaseCaseId INNER JOIN
PhdRpt.RptMonthlyProduction_542 AS rmp ON rcl.ReportRunCaseId = rmp.ReportRunCaseId`
GROUP BY rmp.EcoDate
You can do this by enumerating the rows within a year. Then update all but the first:
with toupdate as (
select t.*, row_number() over (partition by [year] order by [date]) as seqnum
from t
)
update toupdate
set [year] = NULL
where seqnum > 1;
If you want this as a select statement:
with ts as (
select t.*, row_number() over (partition by [year] order by [date]) as seqnum
from t
)
select [date],
(case when seqnum = 1 then [year] end) as [year]
from ts;