I want to look if there are more than one QuestionCategory in one day in the table contentment. In my case people don't need to answer in one day questions with different categories. I can make a trigger of this.
The contentmenttable: employeeid, questionid, date, score
The questiontable: questionid, questioncat, question
Data contentmenttable: 1, 1, 11-18-2018, 4
Data questiontable: 1, Work, How is your job? 2, Work, Are you happy
with your job?
If have something like this:
select c.questionid, date
from contentment c
join question q
on c.questionid= q.questionid
group by c.questionid, date
having count(questioncat) >= 2
But this query is only counting IF a questionid is two times or more in this table, not if there are two different questioncategories in this table.
I use SQL Server.
So if someone wants to insert this:
insert into contentment values (1, 2, 11-18-2018', null) (null because employee needs to give a score)
The query needs to give this questionid and date (2 and 11-18-2018), because it is the same questioncat "work" on the same day 11-18-2018.
You need to add DISTINCT:
select c.questionid, date
from contentment c
join question q
on c.questionid= q.questionid
group by c.questionid, date
having count(DISTINCT questioncat) >= 2;
-- counting only different questioncat
Your question is hard to follow, but I think you want employees that have more than one question category in a given day. If so:
select c.employeeid, c.date, count(distinct q.questioncat)
from contentment c join
question q
on c.questionid = q.questionid
group by c.employeeid, c.date
having count(distinct q.questioncat) >= 2;
Related
Struggling to go the extra step with a SQL query I'd like to run.
I have a customer database with a Customer table with the date/time detail of when the customer joined and a transaction table with details of their transactions of the years
What I'd like to do is to Group by the Join Date (as Year) and count the number that joined in each year then in the next column I'd like to then count the number who have transacted in a specific year E.g. 2016 the current year. This way I can show customer retention over the years.
Both tables are linked by a customer URN, but I am struggling to get my head around the the most efficient way to show this. I can easily count and group the members by joined year and I can display the max dated transaction but I am struggling to bring the two together. I think I need to use sub queries and a left join but it's alluding me.
Example output column headers with data
Year_Joined = 2009
Joiner_Count = 10
Transact_in_2016 = 5
Where I am syntax-wise. I know this is no where near complete. As I need to group by DateJoined and then sub query the count of customers of have transacted in 2016?
SELECT Customer.URNCustomer,
MAX(YEAR(Customer.DateJoined)),
MAX(YEAR(Tran.TranDate)) As Latest_Tran,
FROM Mydatabase.dbo.Customer
LEFT JOIN Mydatabase.dbo.Tran
ON Tran.URNCustomer = Customer.URNCustomer
GROUP BY Customer.URNCustomer
ORDER BY Customer.URNCustomer
The best approach is to do the aggregation before doing the joins. You want to count two different things, so count them individually and them combine them.
The following uses full outer join. This handles the case where there are years with no new customers and years with no transactions:
select coalesce(c.yyyy, t.yyyy) as yyyy,
coalesce(c.numcustomers, 0) as numcustomers,
coalesce(t.numtransactions, 0) as numtransactions
from (select year(c.datejoined) as yyyy, count(*) as numcustomers
from Mydatabase.dbo.Customer c
group by year(c.datejoined)
) c full outer join
(select year(t.trandate) as yyyy, count(*) as numtransactions
from database.dbo.Tran t
group by year(t.trandate)
) t
on c.yyyy = t.yyyy;
You may want to try something like this:
SELECT YEAR(Customer.DateJoined),
COUNT( Customer.URNCustomer ),
COUNT( DISTINCT Tran.URNCustomer ) AS NO_ACTIVE_IN_2016
FROM Mydatabase.dbo.Customer
LEFT Mydatabase.dbo.Tran
ON Tran.URNCustomer = Customer.URNCustomer
AND YEAR(Tran.TranDate) = 2016
GROUP BY YEAR(Customer.DateJoined)
If I have a customer respond to the same survey in 30 days more than once, I only want to count it once. Can someone show me code to do that please?
create table #Something
(
CustID Char(10),
SurveyId char(5),
ResponseDate datetime
)
insert #Something
select 'Cust1', '100', '5/6/13' union all
select 'Cust1', '100', '5/13/13' union all
select 'Cust2', '100', '4/20/13' union all
select 'Cust2', '100', '5/22/13'
select distinct custid, SurveyId, Count(custid) as CountResponse from #Something
group by CustID, SurveyId
The above code only gives me the total count of Response, not sure how to code to count only once per 30 day period.
The output I'm looking for should be like this:
CustomerID SurveyId CountResponse
Cust1 100 1
Cust2 100 2
Going on the theory that you want your periods calculated as 30 days from the first time a survey is submitted, here is a (gross) solution.
declare #Something table
(
CustID Char(10),
SurveyId char(5),
ResponseDate datetime
)
insert #Something
select 'Cust1', '100', '5/6/13' union all
select 'Cust1', '100', '5/13/13' union all
select 'Cust1', '100', '7/13/13' union all
select 'Cust2', '100', '4/20/13' union all
select 'Cust2', '100', '5/22/13' union all
select 'Cust2', '100', '7/20/13' union all
select 'Cust2', '100', '7/24/13' union all
select 'Cust2', '100', '9/28/13'
--SELECT CustID,SurveyId,COUNT(*) FROM (
select a.CustID,a.SurveyId,b.ResponseStart,--CONVERT(int,a.ResponseDate-b.ResponseStart),
CASE
WHEN CONVERT(int,a.ResponseDate-b.ResponseStart) > 30
THEN ((CONVERT(int,a.ResponseDate-b.ResponseStart))-(CONVERT(int,a.ResponseDate-b.ResponseStart) % 30))/30+1
ELSE 1
END CustomPeriod -- defines periods 30 days out from first entry of survey
from #Something a
inner join
(select CustID,SurveyId,MIN(ResponseDate) ResponseStart
from #Something
group by CustID,SurveyId) b
on a.SurveyId=b.SurveyId
and a.CustID=b.CustID
group by a.CustID,a.SurveyId,b.ResponseStart,
CASE
WHEN CONVERT(int,a.ResponseDate-b.ResponseStart) > 30
THEN ((CONVERT(int,a.ResponseDate-b.ResponseStart))-(CONVERT(int,a.ResponseDate-b.ResponseStart) % 30))/30+1
ELSE 1
END
--) x GROUP BY CustID,SurveyId
At the very least you'd probably want to make the CASE statement a function so it reads a bit cleaner. Better would be defining explicit windows in a separate table. This may not be feasible if you want to avoid situations like surveys returned at the end of period one followed by another in period two a couple days later.
You should consider handling this on input if possible. For example, if you are identifying a customer in an online survey, reject attempts to fill out a survey. Or if someone is mailing these in, make the data entry person reject it if one has come within 30 days.
Or, along the same lines as "wild and crazy", add a bit and an INSERT trigger. Only turn the bit on if no surveys of that type for that customer found within the time period.
Overall, phrasing the issue a little more completely would be helpful. However I do appreciate the actual coded example.
I'm not a SQL Server guy, but in Oacle if you subtract integer values from a 'date', you're effectively subtracting "days," so something like this could work:
SELECT custid, surveyid
FROM Something a
WHERE NOT EXISTS (
SELECT 1
FROM Something b
WHERE a.custid = b.custid
AND a.surveyid = b.surveyid
AND b.responseDate between a.responseDate AND a.responseDate - 30
);
To get your counts (if I udnerstand what you're asking for):
-- Count of times custID returned surveyID, not counting same
-- survey within 30 day period.
SELECT custid, surveyid, count(*) countResponse
FROM Something a
WHERE NOT EXISTS (
SELECT 1
FROM Something b
WHERE a.custid = b.custid
AND a.surveyid = b.surveyid
AND b.responseDate between a.responseDate AND a.responseDate - 30
)
GROUP BY custid, surveyid
UPDATE: Per the case raised below, this actually wouldn't quite work. What you should probably do is iterate through your something table and insert the rows for the surveys you want to keep in a results table, then compare against the results table to see if there's already been a survey received in the last 30 days you want considered. I could show you how to do something like this in oracle PL/SQL, but I don't know the syntax off hand for SQL server. Maybe someone else who knows sql server wants to steal this strategy to code up an answer for you, or maybe this is enough for you to go on.
Call me wild and crazy, but I would solve this problem by storing more state with each survey. The approach I would take is to add a bit type column that indicates whether a particular survey should be counted (i.e., a Countable column). This solves the tracking of state problem inherent in solving this relationally.
I would set values in Countable to 1 upon insertion, if no survey with the same CustID/SurveyId can be found in the preceding 30 days with a Countable set to 1. I would set it to 0, otherwise.
Then the problem becomes trivially solvable. Just group by CustID/SurveyId and sum up the values in the Countable column.
One caveat of this approach is that it imposes that surveys must be added in chronological order and cannot be deleted without a recalculation of Countable values.
Here's one way to handle it I believe. I tested quickly, and it worked on the small sample of records so I'm hopeful it will help you out. Best of luck.
SELECT s.CustID, COUNT(s.SurveyID) AS SurveyCount
FROM #something s
INNER JOIN (SELECT CustID, SurveyId, ResponseDate
FROM (SELECT #Something.*,
ROW_NUMBER() OVER (PARTITION BY custid ORDER BY ResponseDate ASC) AS RN
FROM #something) AS t
WHERE RN = 1 ) f ON s.CustID = f.CustID
WHERE s.ResponseDate BETWEEN f.ResponseDate AND f.ResponseDate+30
GROUP BY s.CustID
HAVING COUNT(s.SurveyID) > 1
Your question is ambiguous, which may be the source of your difficulty.
insert #Something values
('Cust3', '100', '1/1/13'),
('Cust3', '100', '1/20/13'),
('Cust3', '100', '2/10/13')
Should the count for Cust3 be 1 or 2? Is the '2/10/13' response invalid because it was less than 30 days after the '1/20/13' response? Or is the '2/10/13' response valid because the '1/20/13' is invalidated by the '1/1/13' response and therefore more than 30 days after the previous valid response?
The code below is one approach which yields your example output. However, if you add a select 'Cust1', '100', '4/20/13', the result will still be Cust1 100 1 because they are all within 30 days of each prior survey response and so only the first one would be counted. Is this the desired behavior?
SELECT CustID, SurveyID, COUNT(*) AS CountResponse
FROM #SurveysTaken
WHERE (NOT EXISTS
(SELECT 1
FROM #SurveysTaken AS PriorSurveys
WHERE (CustID = #SurveysTaken.CustID)
AND (SurveyId = #SurveysTaken.SurveyId)
AND (ResponseDate >= DATEADD(d, - 30, #SurveysTaken.ResponseDate))
AND (ResponseDate < #SurveysTaken.ResponseDate)))
GROUP BY CustID, SurveyID
Alternatively, you could break the year into arbitrary 30 day periods, resetting with each new year.
SELECT CustID, SurveyID, COUNT(*) AS CountResponse
FROM (SELECT DISTINCT CustID, SurveyID, YEAR(ResponseDate) AS RepsonseYear,
DATEPART(DAYOFYEAR, ResponseDate) / 30 AS ThirtyDayPeriod
FROM #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID
You could also just go by month.
SELECT CustID, SurveyID, COUNT(*) AS CountResponse
FROM (SELECT DISTINCT CustID, SurveyID, YEAR(ResponseDate) AS ResponseYear,
MONTH(ResponseDate) AS ResponseMonth
FROM #SurveysTaken) AS SurveysByMonth
GROUP BY CustID, SurveyID
You could use 30 day periods from an arbitrary epoch date. (Perhaps by pulling the date the survey was first created from another query?)
SELECT CustID, SurveyID, COUNT(*) AS CountResponse
FROM (SELECT DISTINCT CustID, SurveyID, DATEDIFF(D, '1/1/2013', ResponseDate) / 30 AS ThirtyDayPeriod
FROM #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID
One final variation on arbitrary thirty periods is to base them on the first time the customer ever responded to the survey in question.
SELECT CustID, SurveyID, COUNT(*) AS CountResponse
FROM (SELECT DISTINCT CustID, SurveyID, DATEDIFF(DAY,
(SELECT MIN(ResponseDate)
FROM #SurveysTaken AS FirstSurvey
WHERE (CustID = #SurveysTaken.CustID)
AND (SurveyId = #SurveysTaken.SurveyId)), ResponseDate) / 30 AS ThirtyDayPeriod
FROM #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID
There is one issue that you run into with the epoch/period trick which is that the counted surveys occur only once per period but aren't necessarily 30 days apart.
I have two tables, TblVal and TblAdj.
In TblVal I have a bunch of values that I need adjusted according to TblAdj for a given TblVal.PersonID and TblVal.Date and then returned in some ViewAdjustedValues. I must apply only those adjustments where TblAdj.Date >= TblVal.Date.
The trouble is that since all the adjustments are either a subtraction or a division, they need to be made in order. Here is the table structure:
TblVal: PersonID, Date, Value
TblAdj: PersonID, Date, SubtractAmount, DivideAmount
I want to return ViewAdjustedValues: PersonID, Date, AdjValue
Can I do this without iterating through TblAdj using a WHILE loop and an IF block to either subtract or divide as necessary? Is there some nested SELECT table magic I can perform that would be faster?
I think you can do it without a loop, but whether you want to or not is another question. A query that I think works is below (SQL Fiddle here). The key ideas are as follows:
Each SubtractAmount has the ultimate effect of subtracting SubtractAmount divided by the product of all later DivideAmounts for the same PersonID. The Date associated with the PersonID isn't relevant to this adjustment (fortunately). The CTE AdjustedAdjustments contains these adjusted SubtractAmount values.
The initial Value for a PersonID gets divided by the product of all DivideAmount values on or after that persons Date.
EXP(SUM(LOG(x))) works as an aggregate product if all values of x are positive. You should constrain your DivideAmount values to assure this, or adjust the code accordingly.
If there are no DivideAmounts, the associated product is NULL and changed to 1. Similarly, NULL sums of adjusted SubtractAmount values are changed to zero. A left join is used to preserve an values that are not subject to any adjustments.
SQL Server 2012 supports an OVER clause for aggregates, which was helpful here to aggregate "all later DivideAmounts."
WITH AdjustedAdjustments AS (
select
PersonID,
Date,
SubtractAmount/
EXP(
SUM(LOG(COALESCE(DivideAmount,1)))
OVER (
PARTITION BY PersonID
ORDER BY Date
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
) AS AdjustedSubtract,
DivideAmount
FROM TblAdj
)
SELECT
p.PersonID,
p.Value/COALESCE(EXP(SUM(LOG(COALESCE(DivideAmount,1)))),1)
-COALESCE(SUM(a.AdjustedSubtract),0) AS AmountAdjusted
FROM TblVal AS p
LEFT OUTER JOIN AdjustedAdjustments AS a
ON a.PersonID = p.PersonID
AND a.Date >= p.Date
GROUP BY p.PersonID, p.Value, p.Date;
Try something like following:
with CTE_TblVal (PersonID,Date,Value)
as
(
select A.PersonID, A.Date, A.Value
from TblVal A
inner join TblAdj B
on A.PersonID = B.PersonID
where B.Date >= A.Date
)
update CTE_TblVal
set Date = TblAdj.Date,
Value = TblAdj.Value
from CTE_TblVal
inner join TblAdj
on CTE_Tblval.PersonID = TblAdj.PersonID
output inserted.* into ViewAdjustedValues
select * from ViewAdjustedValues
I have three tables Books, Debit and Client.
Books (Id, Title, ...)
Debit (BooksID, ClientId, BorrowingDate,....)
Client (Id, Name, ...)
I need SQL query that will return
a book that is most times rented out, between two dates.
Dates will be passed as parameters to query.
How about this?
SELECT * FROM Books INNER JOIN Debit ON Books.Id = Debit.BooksID WHERE BorrowingDate >= #StartDate AND BorrowingDate <= #EndDate
If you edit in an example of what you would like the output to look like I can redefine the above query to help you get what you want.
And for the record, Adrian is right; it is generally better to make an attempt yourself and come to us with a problem, rather than asking SO to solve the problem for you. But hopefully what I have here will get you started. Welcome to SO! :)
Edit
It also just occurred to me that you might be looking for the book which was rented out most number of times between your two dates... If so, try this:
SELECT TOP 1 Books.* FROM (
SELECT COUNT(*) AS DebitCount, BooksID FROM Debit WHERE BorrowingDate >= #StartDate AND BorrowingDate <= #EndDate GROUP BY BooksID
) Debits INNER JOIN Books ON Books.ID = Debits.BooksID
ORDER BY DebitCount DESC
Hope that helps!
The query...
SELECT TOP 1 *
FROM (
SELECT BooksID, COUNT(*) AS C
FROM Debit
WHERE BorrowingDate BETWEEN #min_date and #max_date
GROUP BY BooksID
)
ORDER BY C DESC
...will yield one row containing the BooksID and number of rents of the book that has been rented most times between #min_date and #max_date.
You can later easily JOIN with Books if you need more than just BooksID.
here is the current complex query given below.
SELECT DISTINCT Evaluation.ETCode, Training.TTitle, Training.Tcomponent, Training.TImpliment_Partner, Training.TVenue, Training.TStartDate, Training.TEndDate, Evaluation.EDate, Answer.QCode, Answer.Answer, Count(Answer.Answer) AS [Count], Questions.SL, Questions.Question
FROM ((Evaluation INNER JOIN Training ON Evaluation.ETCode=Training.TCode) INNER JOIN Answer ON Evaluation.ECode=Answer.ECode) INNER JOIN Questions ON Answer.QCode=Questions.QCode
GROUP BY Evaluation.ETCode, Answer.QCode, Training.TTitle, Training.Tcomponent, Training.TImpliment_Partner, Training.Tvenue, Answer.Answer, Questions.Question, Training.TStartDate, Training.TEndDate, Evaluation.EDate, Questions.SL
ORDER BY Answer.QCode, Answer.Answer;
There is an another column Training.TCode. I need to count distinct Training.TCode, can anybody help me?
If you need more information please let me know
try
select ..., count(distinct Training.Tcode) as ..., ...
EDIT - please now look at this...
Take the following SQL code. The first select is how SQL server would do this and the second query should be access compliant...
declare #t table (eCode int, tcode int)
insert into #t values(1,1)
insert into #t values(1,1)
insert into #t values(1,2)
insert into #t values(1,3)
insert into #t values(2,2)
insert into #t values(2,3)
insert into #t values(3,1)
select
ecode, count(distinct tCode) countof
from
#t
group by
ecode
select ecode, count(*)
from
(select distinct tcode, ecode
from #t group by tcode, ecode) t
group by ecode
It returns the following:
ecode tcode
1 3 (there are 3 distinct tcode for ecode of 1)
2 2 (there are 2 distinct tcode for ecode of 2)
3 1 (there is 1 distinct tcode for ecode of 3)
I posted a similar question about a year ago in Google groups. I received an excellent answer:
A crosstab can do (from an original proposition from Steve Dassin) as long
as you count either the fund, either the subfund:
TRANSFORM COUNT(*) AS theCell
SELECT ValDate,
COUNT(*) AS StandardCount,
COUNT(theCell) AS DistinctCount
FROM tableName
GROUP BY ValDate
PIVOT fund IN(Null)
which, for each day (group), will return the number of records and the
number of different (distinct) funds.
Change
PIVOT fund IN(Null)
to
PIVOT subfund IN(Null)
to get the same, for sub-funds.
Hoping it may help,
Vanderghast, Access MVP
I don't know if that will work, but here's a link to that post.
Sadat, use a subquery like this:
SELECT DISTINCT Evaluation.ETCode, Training.TTitle, Training.Tcomponent, Training.TImpliment_Partner, Training.TVenue, Training.TStartDate, Training.TEndDate, Evaluation.EDate, Answer.QCode, Answer.Answer, Count(Answer.Answer) AS [Count], Questions.SL, Questions.Question,
(SELECT COUNT(*) FROM Training t2 WHERE t2.TCode = Evalution.ETCode) as TCodeCount
FROM ((Evaluation INNER JOIN Training ON Evaluation.ETCode=Training.TCode) INNER JOIN Answer ON Evaluation.ECode=Answer.ECode) INNER JOIN Questions ON Answer.QCode=Questions.QCode
GROUP BY Evaluation.ETCode, Answer.QCode, Training.TTitle, Training.Tcomponent, Training.TImpliment_Partner, Training.Tvenue, Answer.Answer, Questions.Question, Training.TStartDate, Training.TEndDate, Evaluation.EDate, Questions.SL
ORDER BY Answer.QCode, Answer.Answer;
I managed to do a count distinct value in Access by doing the following:
select Job,sum(pp) as number_distinct_fruits
from
(select Job, Fruit, 1 as pp
from Jobtable group by Job, Fruit) t
group by Job
You have to be careful as if there is a blank/null field (in my code fruit field) the group by will count that as a record. A Where clause in the inner select will ignore those though.
I've put this on my blog, but am concerned that I've discovered the answer too easily - others here seem to think that you need two sub queries to make this work. Is my solution viable?
Distinct groupings in Access
Have a look at this blog entry, it appears you can do this with subqueries....
http://blogs.msdn.com/access/archive/2007/09/19/writing-a-count-distinct-query-in-access.aspx
I would propose
select R_rep,sum(pp) as number_distinct_Billnos from (select R_rep, Billno, 1 as pp from `Vat_Sales` group by R_rep, Billno) t group by R_rep
try this:
SELECT DISTINCT e.ETCode, t.TTitle, t.Tcomponent,
t.TImpliment_Partner, t.TVenue, t.TStartDate,
t.TEndDate, e.EDate, a.QCode, a.Answer,
q.SL, q.Question,
Count(a.Answer) AnswerCount,
Min(Select Count(*)
From (Select Distinct TCode From Training) As Z ) TCodeCount
FROM Evaluation As e
JOIN Training AS t ON e.ETCode=t.TCode
JOIN Answer AS a ON e.ECode=a.ECode
JOIN Questions AS q ON a.QCode=q.QCode
GROUP BY e.ETCode, a.QCode, t.TTitle, t.Tcomponent,
t.TImpliment_Partner, t.Tvenue, a.Answer, q.Question,
t.TStartDate, t.TEndDate, Evaluation.EDate, q.SL
ORDER BY a.QCode, a.Answer;