I want to know how I can write a SQL query to get the count of users who participated in each month survey
The table structures are like below
SurveyTable
SurveyId, SurveyName, Datetime
SurveyInfo
Id, SurveyId, First name, Last name DateTime
Sample Data
SurveyTable
1 Survey-ABC 1 Jan 2011
2 Survey-AXC 1 Feb 2011
3 Survey-AEW 1 Mar 2011
4 Survey-AVD 1 Apr 2011
....
SurveyInfo
1 1 Peter James 1 Jan 2011
2 1 John Tom 1 Jan 2011
3 1 Harry Kin 1 Jan 2011
4 2 Amber Wills 1 Feb 2011
5 2 Tom Kin 1 Feb 2011
I want a result like
3 users participated in Survey-ABC,
2 users participated in Survey-AXC
Thanks
SELECT
Count(*) AS "Number of participants",
SurveyTable.surveyName
FROM
surveyinfo
INNER JOIN surveytable
ON surveytable.surveyid = surveyinfo.surveyid
GROUP BY
Year(DateTime),
Month(Date_Time)
SELECT stab.Name, COUNT(si.ID)
FROM SurveyTable as stab INNER JOIN SurveyInfo si ON si.SurveyId = stab.SurveyId
GROUP BY stab.Name
Because you want to see the number or participant for each month, so you need to extract the month and group by this.
SELECT SurveyTable.SurveyId, SurveyTable.SurveyName, Month(SurveyInfo.DateTime) As M, COUNT(SurveyInfo.*) As Cnt
FROM SurveyTable
LEFT JOIN SurveyInfo ON SurveyInfo.SurveyId = SurveyTable.SurveyId
GROUP By SurveyTable.SurveyId, SurveyTable.SurveyName, Month(SurveyInfo.DateTime)
The following select statement
SELECT st.Name, COUNT(si.ID)
FROM SurveyTable.st INNER JOIN SurveyInfo si ON si.SurveyId = st.SurveyId
GROUP BY st.Name
should provide you with the users who participated in the surveys.
Related
What I'm trying to do in this case is to get the ''most future'' record of a Bills table and get all the record prior 13 months from that last record, so what I've tried is something like this
SELECT
users.name,
EXTRACT(month from priority_date) as month,
EXTRACT(year from priority_date) as year,
SUM("money_balance") as "money_balance"
FROM bills
JOIN users on users.id = bills.user_id
WHERE priority_date >= ( SELECT
DATE_TRUNC('month', MAX(debts.priority_date))
FROM bills
INNER JOIN users ON bills.property_id = users.id
WHERE users.company_id = 15
AND users.active = true
AND bills.paid = false ) - interval '13 month'
AND priority_date <= ( SELECT
MAX(bills.priority_date)
FROM bills
INNER JOIN users ON bills.property_id = users.id
WHERE users.community_id = 15
AND users.active = true
AND debts.paid = false )
AND users.company_id = 15
AND bills.paid = false
AND users.active = true
GROUP BY 1,2,3
ORDER BY year, month
So for instance, lets say the most future date for a created bill is December 2022, this query will give me the info from November 2021 to December 2022
The data will give me something like
name
month
year
money_balance
Joshua..
11
2021
300
Joshua..
1
2022
111
Mark..
1
2022
200
...
...
...
...
John
12
2022
399
In the case of Joshua, because he had no bills to pay in December 2021, it doesn't return anything for that month/year.
Is it possible to return the months/year where there are no records for that month, for each user?
Something like
name
month
year
money_balance
Joshua..
11
2021
300
Joshua..
12
2021
0
Joshua..
1
2022
111
other users
....
...
...
Thank you so much!
We can use a CTE to create the list of months, using the maximum and minimum dates from bill, and then cross join it onto users to get a line for all users for all months. We then left join onto bills to populate the last column.
The problem with this approach is that we can end up with a lot of rows with no value.
create table bills(user_id int,priority_date date, money_balance int);
create table users(id int, name varchar(25));
insert into users values(1,'Joshua'),(2,'Mark'),(3,'John');
insert into bills values(1,'2021-11-01',300),(1,'2022-01-01',111),(2,'2022-01-01',200),(3,'2021-12-01',399);
;with months as
(SELECT to_char(generate_series(min(priority_date), max(priority_date), '1 month'), 'Mon-YY') AS "Mon-YY"
from bills)
SELECT
u.name,
"Mon-YY",
--EXTRACT(month from "Mon-YY") as month,
--EXTRACT(year from "Mon-YY") as year,
SUM("money_balance") as "money_balance"
FROM months m
CROSS JOIN users u
LEFT JOIN bills b
ON u.id = b.user_id
AND to_char(priority_date,'Mon-YY') = m."Mon-YY"
GROUP BY
u.name,
"Mon-YY"
ORDER BY "Mon-YY", u.name
name | Mon-YY | money_balance
:----- | :----- | ------------:
John | Dec-21 | 399
Joshua | Dec-21 | null
Mark | Dec-21 | null
John | Jan-22 | null
Joshua | Jan-22 | 111
Mark | Jan-22 | 200
John | Nov-21 | null
Joshua | Nov-21 | 300
Mark | Nov-21 | null
db<>fiddle here
table 1 is maintable_KQPPJ : contains GroupID, Year, Name, VendorID. This table contains multiple records with the same GroupID
table 2 is cb_vendorinformation: contains GroupID and CompanyName
I would like to join both tables on GroupID. The output should only have GroupID, CompanyName, and Count. The Count is the distinct count of GroupID in maintable_KQPPJ.
I have the following code but it doesn't really give me the output I'm looking for.
SELECT maintable_KQPPJ.GROUPID, cb_vendorinformation.CompanyName, count(distinct maintable_KQPPJ.GROUPID)
FROM maintable_KQPPJ
JOIN cb_vendorinformation ON maintable_KQPPJ.GROUPID=cb_vendorinformation.GROUPID
maintable_KQPPJ:
GroupID Year VendorID Name
26 2019 9999 John
26 2020 2345 Jane
6 2018 3244 Jack
36 2021 3245 Jill
cb_vendorinformation:
GroupID CompanyName
26 Walmart
6 Target
36 Kroger
The output should look like
GroupID CompanyName Count
26 Walmart 2
6 Target 1
36 Kroger 1
You need group by and count(*)
SELECT maintable_KQPPJ.GROUPID
, cb_vendorinformation.CompanyName
, count(*)
FROM maintable_KQPPJ
JOIN cb_vendorinformation ON maintable_KQPPJ.GROUPID=cb_vendorinformation.GROUPID
GROUP BY maintable_KQPPJ.GROUPID
, cb_vendorinformation.CompanyName
My real life problem is more comlpex, but the issue can be washed down to the following: I have one table with clients, and another table with clients' bank accounts. Now I want to do a select returning all clients that have bank accounts, joining with the bank account that was used least recently, ordered by the BANK_ID.
CLIENT:
CLIENT_ID NAME
-----------------
1 JOE
2 BEN
3 SUE
BANK_ACCOUNT:
BANK_ID CLIENT_ID LAST_USED
-----------------------------------
1 2 Jan 1 2020
2 1 Mar 15 2020
3 2 Aug 5 2020
4 1 Feb 7 2020
5 1 Oct 13 2020
So Joe has three bank accounts, Ben has two, and Sue does not have any.
The select should contain only Joe and Ben. Ben should go first because his "oldest" bank account is on BANK_ID 1 while Joe's is on BANK_ID 4.
BANK_ID CLIENT_ID NAME
---------------------------
1 2 BEN
4 1 JOE
I've been playing around with joins and subqueries, but I'm not sure what would be the best way to accomplish this query.
Thank you
A typical method is to use row_number():
select ba.bank_id, ba.client_id, c.name
from client c join
(select ba.*,
row_number() over (partition by client_id order by last_used) as seqnum
from bank_account ba
) ba
on ba.client_id = c.client_id and ba.seqnum = 1
order by ba.bank_id;
In Oracle, you can also use aggregation:
select max(ba.bank_id) keep (dense_rank over order by ba.last_used) as bank_id,
ba.client_id, c.name
from bank_account ba
on ba.client_id = c.client_id
group by ba.client_id, c.name
order by ba.bank_id;
One option uses a lateral join:
select ba.bank_id, ba.client_id, c.name
from client c
cross apply (
select ba.*
from bank_account ba
where ba.client_id = c.client_id
order by ba.last_used
fetch first row only
) ba
The upside is that you can easily bring more columns from the bank account tables (say you want the last_used date for example), by just expanding the select clause.
i got a dataset which looks like this:
customernumber year value
1 2011 500
2 2011 100
1 2010 400
3 2010 600
3 2011 300
2 2010 700
i want it to be ordered by highest value of year 2011, but the rows of each customer need to stay together.
it should look like this:
customernumber year value
1 2011 500
1 2010 400
3 2011 300
3 2010 600
2 2011 100
2 2010 700
is this even possible?
thanks in advance!
Use join to bring that value in, and then you can use it for the oder by:
select d.customernumber, d.year, d.value
from dataset d join
(select d.*
from dataset d
where d.year = 2011
) d2011
on d.customernumber = d2011.customernumber
order by d2011.value, d.customernumber, d.year desc;
In databases that support window functions, this can more easily be done as:
select d.*
from dataset d
order by max(case when year = 2011 then value end) over (partition by customernumber),
customernumber, year desc;
select customernumber, year, value from mytable
group by customernumber, year, value
order by year desc
I have a table (Vehicles) which contains a list of vehicles.
VehicleID
PlateNo
CurrentDriver
I also have a table (History) which contains a the driver history for the vehicles:
HistoryID
VehicleID
ReceivedDate (vehicle receiving date)
DriverName
I have another table (Repairs) which contains the repairs for all the vehicles:
RepairID
VehicleID
RepairDate
RepairCost
Using SQL Server and based on the History table, I want to get all the RepairCost values between two dates for a given DriverName.
For example, I want to get all the RepairCost values for driver 'John Doe', between 01.01.2013 and 01.05.2013, who was allocated to three different vehicles in that period.
My query so far is:
SELECT H.DriverName, R.RepairCost, R.RepairDate
FROM Repairs AS R
INNER JOIN Vehicles AS V ON R.VehicleID = V.VehicleID
INNER JOIN History H ON H.VehicleID = V.VehicleID
WHERE H.DriverName = 'John'
AND R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
There's also some sample data in a SQL Fiddle.
The problem seems to be that I'm getting all the results twice.
LATER EDIT:
My progress so far:
DECLARE #Driver varchar(50),#StartDt datetime, #EndDt datetime
SELECT #Driver = 'John Doe',#StartDt = '20130101' ,#EndDt = '20130501'
;With VehicleAllocation
AS
(
SELECT h.*,h1.ChangeDate
FROM History h
OUTER APPLY (SELECT MIN(ReceivedDate) AS ChangeDate
FROM History
WHERE VehicleID = h.VehicleID
AND DriverName <> h.DriverName
AND ReceivedDate > h.ReceivedDate
)h1
WHERE h.DriverName = #Driver
)
SELECT *
FROM VehicleAllocation h
INNER JOIN Repairs r
ON r.VehicleID = h.VehicleID
WHERE DriverName = #Driver
AND RepairDate > = #StartDt
AND RepairDate < #EndDt + 1
AND RepairDate BETWEEN h.ReceivedDate AND COALESCE(h.ChangeDate,RepairDate)
I discoverd a problem with the line 'AND DriverName <> h.DriverName'. Why is that line useful? If I had the same driver name, one after the other, in the History table, it skipped to the last car delivery date for that driver name.
Sample data:
'History' table
ReceivedDate DriverName
04.11.2013 Mike
13.11.2013 Dan
15.11.2013 Dan
17.11.2013 Ryan
20.11.2013 Dan
22.11.2013 Ryan
25.11.2013 Mike
26.11.2013 Dan
29.11.2013 Ryan
04.12.2013 Dan
'Repairs' table
RepairDate RepairCost
05.11.2013 2615.30
14.11.2013 135.66
16.11.2013 4913.04
18.11.2013 538.92
21.11.2013 152.48
23.11.2013 5946.89
26.11.2013 3697.64
27.11.2013 734.01
30.11.2013 279.62
Query result
RepairDate RepairCost
07.11.2013 380.00
14.11.2013 135.66
16.11.2013 4913.04
16.11.2013 4913.04
21.11.2013 152.48
27.11.2013 734.01
As you can see in the query result, line 3 and 4 have the same value/date.
The query interval was 01-01-2013 <-> 31-12-2013.
Also, what if I want to get the SUM of different colums from different tables?
For example, SUM(Total) column from 'Repairs' table, SUM(Value) column from 'Tires' table...
How can I adapt the script?
Thanks!
I have no idea why you include you Vehicle table in your query, as you don't want any information from there.
You are getting "double" results because you match every Repair (e.g. the one on jan 15th) with every record with the same Vehicle id is History (there are three of those!). Two of those matches are for drive John, so you get two results.
What you want is to match only on the driver that, according to your history table, was the drive at the time of the repair!
So, I first matched each repairdate with the actually matching Receiveddate in the history table:
SELECT R1.Repairdate, Max(H1.ReceivedDate) as ReceivedDate
FROM Repairs R1
JOIN History H1
ON R1.VehicleID=H1.VehicleID
AND H1.ReceivedDate < R1.RepairDate
GROUP BY R1.RepairDate
I then used that query in a join to receive the wanted data:
SELECT R.RepairDate, H.DriverName, R.RepairCost
FROM Repairs AS R
JOIN History H ON R.VehicleID=H.VehicleID
JOIN (
SELECT R1.Repairdate, Max(H1.ReceivedDate) as ReceivedDate
FROM Repairs R1
JOIN History H1
ON R1.VehicleID=H1.VehicleID
AND H1.ReceivedDate < R1.RepairDate
GROUP BY R1.RepairDate)
AS H2
ON R.Repairdate = H2.Repairdate
AND H.ReceivedDate = H2.Receiveddate
WHERE R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
AND H.DriverName = 'John'
This returns me 6 records : http://sqlfiddle.com/#!3/fcebf/62
As a sanity check, leave out the complete WHERE on date and name and include the vehicle number in teh select.
You will get 14 repairs listed with the name of the driver who was drivign the vehicle at that time. You can easily confirm that the driver linked to the vehicle at that time is correct according to your History data:
DRIVERNAME VEHICLEID REPAIRDATE REPAIRCOST
John 1 January, 15 2013 00:00:00+0000 10
Ryan 2 January, 18 2013 00:00:00+0000 15
Ryan 2 January, 22 2013 00:00:00+0000 15
John 1 February, 03 2013 00:00:00+0000 5
Ryan 2 February, 05 2013 00:00:00+0000 25
John 1 February, 10 2013 00:00:00+0000 10
John 2 February, 26 2013 00:00:00+0000 10
Ryan 1 March, 01 2013 00:00:00+0000 100
John 2 March, 03 2013 00:00:00+0000 30
John 2 March, 08 2013 00:00:00+0000 5
Ryan 1 March, 10 2013 00:00:00+0000 45
Ryan 1 March, 17 2013 00:00:00+0000 25
Ryan 2 March, 25 2013 00:00:00+0000 10
Ryan 2 March, 28 2013 00:00:00+0000 30
if you add HistoryID to your query you will notice that rows are not duplicate, but they have different HistoryID, try this query
SELECT H.DriverName, R.RepairCost, R.RepairDate , H.HistoryID
FROM
Repairs AS R
INNER JOIN Vehicles AS V ON R.VehicleID=V.VehicleID
INNER JOIN History H ON H.VehicleID=V.VehicleID
WHERE H.DriverName='John' AND R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
so I think it's something wrong with your data.
you can eliminate these duplicate rows using DISTINCT , but I recommend you to double check your History table data