Select SQL with conditions to count different values - sql-server

I'm trying to do a query on SQL but I'm stuck!
I have two tables:
Table 1: Question
Table 2: Answer
For each question I can have one or more answers from different users but each user can comment one time.
When a user answers a question, he must choose one status to his answer:
1) Agree, 2) Disagree or 3) Discuss
So, the "Question" table has all the questions like this:
Id Question
1 q1
2 q2
3 q3
..and the "Answer" table has all the answers from the users, plus the FK from the "Question" table and a column with the status chosen by the user.
Id Answer IdQuestion Status
1 a1 1 1
2 a2 1 3
3 a3 2 2
4 a4 2 2
5 a5 3 1
What I need: I need to select all the questions AND I need to count all the questions that has different aswer's status.
Example:
Question 1 has two answers and the two answers has different status. I need to count or put a number just to know that this question has answers with diffent status.
Question 2 has two answers but all the answers has the same status. I don't need to count that.. or maybe put other number to differentiate from the questions that has answers with diffent status.
And the Questions that has only one answer I just select it normally.

Correct me if I'm wrong, but it sounds like you're just after a distinct Status count from the Answers table. So based on a question (regardless of answers) you want to count how many different Status values are present for each question:
CREATE TABLE #Question
(
[Id] INT ,
[Question] VARCHAR(2)
);
INSERT INTO #Question
( Id, Question )
VALUES ( 1, 'q1' ),
( 2, 'q2' ),
( 3, 'q3' ),
( 4, 'q4' )
CREATE TABLE #Answer
(
[Id] INT ,
[Answer] VARCHAR(2) ,
[IdQuestion] INT ,
[Status] INT
);
INSERT INTO #Answer
( [Id], [Answer], [IdQuestion], [Status] )
VALUES ( 1, 'a1', 1, 1 ),
( 2, 'a2', 1, 3 ),
( 3, 'a3', 2, 2 ),
( 4, 'a4', 2, 2 ),
( 5, 'a5', 3, 1 );
SELECT q.id ,
COUNT(DISTINCT Status) DistinctStatusCount
FROM #Question q
LEFT JOIN #Answer a ON q.Id = a.IdQuestion
GROUP BY q.Id
DROP TABLE #Answer
DROP TABLE #Question
Output
IdQuestion DistinctStatusCount
1 2
2 1
3 1
4 0
If you're only interested with questions that have at least one answer you can simply refer to the Answers table:
SELECT IdQuestion ,
COUNT(DISTINCT Status) DistinctStatusCount
FROM #Answer
GROUP BY IdQuestion

try this:
select q.id,
question,
case when min(status) <> max(status) then
1
else
0
end as hasDifferentStatuses
from questions q
inner join answers a on(q.id = a.IdQuestion)
group by q.id, question

I must be missing something as this seems pretty straight forward... The only reason I included the join is a question may not have any answers and you asked for all questions to be returned. Otherwise this could be a query just on the answer table.
SELECT Q.id, count(Distinct A.status)
FROM Question Q
LEFT JOIN Answer A
on Q.ID = A.IdQuestion
Group by Q.IdQuestion

Related

How to get max row in a table with 3 columns [duplicate]

This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed 7 months ago.
I'm running Microsoft SQL Server 2014 - 12.0.4213.0 (X64).
(Apologies - I'm a newbie and I know I'm running an old version)
I have the following table:
ID
Name
Time
1
Finished
2022-07-13 17:09:48.0000000
1
Start
2022-07-13 17:00:48.0000000
2
Clean
2022-07-13 15:09:48.0000000
2
Waiting
2022-07-13 17:34:48.0000000
2
Clean
2022-07-13 12:09:48.0000000
3
Start
2022-07-12 18:09:48.0000000
3
Middle
2022-07-12 14:09:48.0000000
3
Middle
2022-06-13 17:09:48.0000000
I want to return a group that will show the max time for each ID number, but also return the Name value of that max row.
I can do a
SELECT
ID, MAX(Time)
FROM
...
WHERE
...
GROUP BY
(ID)
but I need to pull in the Name column as well. I just want one row per ID returning the max time for that ID, and the Name associated with that Time & ID number
Any help would be great thank you
This kind of thing has been asked and answered so many times, but finding the right search term can be challenging. Here is how you can tackle this with your sample data.
declare #Something table
(
ID int
, Name varchar(20)
, Time datetime2
)
insert #Something values
(1, 'Finished', '2022-07-13 17:09:48.0000000')
, (1, 'Start', '2022-07-13 17:00:48.0000000')
, (2, 'Clean', '2022-07-13 15:09:48.0000000')
, (2, 'Waiting', '2022-07-13 17:34:48.0000000')
, (2, 'Clean', '2022-07-13 12:09:48.0000000')
, (3, 'Start', '2022-07-12 18:09:48.0000000')
, (3, 'Middle', '2022-07-12 14:09:48.0000000')
, (3, 'Middle', '2022-06-13 17:09:48.0000000')
select ID
, Name
, Time
from
(
select *
, RowNum = ROW_NUMBER()over(partition by s.ID order by s.Time desc)
from #Something s
) x
where x.RowNum = 1
Just another option (a nudge less performant)
Select Top 1 with ties *
From YourTable
Order By row_number() over (partition by ID order by Time desc)
This can also work
select * from table
where time in (select max(time) from table group by id )
But other's answers seem more efficient.
I have not tested this, if it's wrong then will delete the answer.

I need to understand the following MS SQL statement ( SELECT FROM VALUES)

I am pretty new to MS SQL but I am having to work with it a lot now. I need to understand what is going on here:
BEGIN TRANSACTION loadHalfdayAbsences;
INSERT INTO #halfDayAbsences
([AbsencePart],
[AbsenceId],
[DeleteDate],
[LastChangeDate]
)
SELECT CASE ap.AbsencePart
WHEN 1
THEN a.AbsenceStart
WHEN 3
THEN a.AbsenceEnd
ELSE CASE
WHEN a.AbsenceStartHalfDay = 1
THEN DATEADD(DAY, 1, a.AbsenceStart)
ELSE a.AbsenceStart
END
END AS newEnd,
CASE ap.AbsencePart
WHEN 1
THEN 0.50
WHEN 2
THEN 1.00
WHEN 3
THEN 0.50
END AS newDuration,
[ap].[AbsencePart],
[a].[AbsenceId],
[a].[EmployeeId],
FROM
(
SELECT AbsencePart
FROM(VALUES(1), (2), (3)) AS t(AbsencePart)
) AS ap
INNER JOIN dwh.Absence AS a ON 1 = 1
WHERE AbsenceType IN
(
SELECT AbsenceType
FROM #AbsenceType4TimeTac
)
Particularly:
FROM
(
SELECT AbsencePart
FROM(VALUES(1), (2), (3)) AS t(AbsencePart)
) AS ap
INNER JOIN dwh.Absence AS a ON 1 = 1
WHERE AbsenceType IN
(
SELECT AbsenceType
FROM #AbsenceType4TimeTac
)
What does this part do?
What do the VALUES do here for me?
What happens to the data?
Is it being selected, but only fields where there is either a 1, a 2 or a 3 in there? Or what is going on with VALUES?
Thanks for any input in advance :)
The portion
FROM (VALUES(1), (2), (3)) AS t(AbsencePart)
just is an inline table consisting of three values, 1 through 3, in a column called AbsensePart. You could have also used the following syntax:
FROM
(
SELECT 1 AS AbsencePart UNION ALL
SELECT 2 UNION ALL
SELECT 3
) t
That is known as a Table Value Constructor. It is used instead of things like temp tables when you have a list of known values you want in a table. You can read more about them here.

How can I select individual rows related to multiple Ids?

I need to select related Ids from a table based on a list of provided Ids - effectively an Adjacency List problem. I have a working query for a single Id, but it is frankly inelegant at best even though it works! I would welcome suggestions for improvements and for ways to move the single Id solution to a multiple Id solution.
I have a database table like so:
CREATE TABLE [BookingLines]
(
[BookingLineId] BIGINT NOT NULL IDENTITY (138, 1),
[BookingId] BIGINT NOT NULL,
---- Additional Columns Redacted for brevity
[ContractNumber] INT NOT NULL DEFAULT 0,
[ContractSubNumber] DECIMAL NOT NULL DEFAULT 0,
---- Additional Columns Redacted for brevity
);
There will be records in this table, and in some cases there will be 1 or more pairs of records relating to the same Booking Id. The differentiation is in the ContractSubNumber column, where one value in the pair will be n.0 and the other n.1. So if there were three consecutive pairs, the Contract SubNumbers would be:
LineId BookingId SubNumber
1 1 0.0
2 1 0.1
3 1 1.0
4 1 1.1
5 1 2.0
6 1 2.1
I may need to start from the Line Id representing either of the sub numbers, and collect the opposing one. So, if I am starting from LineId 1, I need to retrieve LineId 2 being the related row. I can do this on a single Id using multiple sub selects, like this:
SELECT BookingLineId
FROM
(
SELECT BookingLineId
FROM BookingLines
WHERE BookingId = 1
AND FLOOR(ContractSubNumber) =
(
SELECT FLOOR(ContractSubNumber)
FROM BookingLines
WHERE BookingId = 1 AND BookingLineId = (1)
)
)
WHERE BookingLineId <> 1;
This works correctly, returning the value 2 in this case.
How can I make this more elegant and efficient?
How can I rewrite this to return the opposing values of all Ids in a specified list e.g.
WHERE BookingId = 1 AND BookingLineId IN (1,3,5))
and have it return the result 2,4,6?
All suggestions gratefully received.
EDIT
I have corrected the typo in the SQL provided in the original question, and using the framework proposed by #McNets this is the solution I went for:
SELECT BL.BookingLineId
FROM BookingLines BL
INNER JOIN BookingLines ABL ON ABL.BookingId = BL.BookingId
AND ABL.BookingLineId IN (22, 24, 26)
AND FLOOR(BL.ContractSubNumber) = FLOOR(ABL.ContractSubNumber)
WHERE BL.BookingId = 3 AND BL.BookingLineId NOT IN (22,24,26);
I am very grateful for the contributions and for the final answer. Thanks guys!
As far as there is no information about AgencyBookingLines and no sample data I cannot set up a fiddle example, but I think you can move the AgencyBookingLines subquery to the ON clause.
SELECT BL.BookingLineId
FROM BookingLines BL
INNER JOIN AgencyBookingLines ABL
ON ABL.BookingId = BL.BookinId
AND ABL.BookingLineId = 1
AND FLOOR(BL.ContractSubNumber) = FLOOR(ABL.ContractSubNumber
WHERE BL.BookingId = 1
AND BL.BookingLineId <> 1;
--
-- AND BL.BookingLineId IN (2,4,6);
Will it sub numbers always *.0 & *.1. Then you could try the below
SELECT oppo.*
FROM AgencyBookingLines AS main
INNER JOIN AgencyBookingLines AS oppo ON
oppo.BookingId = main.BookingId
AND oppo.SubNumber <> main.SubNumber
AND FLOOR(oppo.SubNumber) = FLOOR(main.SubNumber)
WHERE main.BookingId = 1
AND main.LineId IN (1,3,5)

How do I look for matching data in SQL?

I have a database table for a todo list application, and i need a way to track tasks which are dependant on other tasks, I already have a table with ID,title, description, IsComplete and a DependsOnTask column, containing the unique identifier for the task another given task is dependant on.
the problem is, when I try the below in SQL it doesn't give any results!
SELECT TOP 1000 [id]
,[title]
,[description]
,[complete]
,[DependsOnTask]
FROM [master].[dbo].[ToDoItems] where ToDoItems.id =ToDoItems.DependsOnTask;
So my question is, is there a way to find all records with a unique identifier matching DependsOnTask?
Thanks in advance :)
You are missing a JOIN:
SELECT tdi.*, dot.*
FROM dbo.ToDoItems tdi JOIN
dbo.ToDoItems dot
ON dot.id = tdi.DependsOnTask;
This returns all tasks where DependsOnTask is not null, along with information from that record.
Notes:
You don't need to use square braces when they are not necessary. They just clutter up queries.
Use table aliases and qualify column names, so you know where columns are coming from.
You need to use an explicit JOIN for references back to the same table.
If you have a hierarchical structure and task could have a parent and that parent is a child to another task, yo can use recursive CTE to find all hierarchy of determined task.
Let me show an example.
You got structure like this:
SELECT *
FROM (VALUES
(1,'Title1','Do some stuff 1', 0, NULL),
(2,'Title2','Do some stuff 2', 0, NULL),
(3,'Title3','Do some stuff 3', 1, 1),
(4,'Title4','Do some stuff 4', 1, 1),
(5,'Title5','Do some stuff 5', 0, 2),
(6,'Title6','Do some stuff 6', 1, 2),
(7,'Title7','Do some stuff 7', 0, 4),
(8,'Title8','Do some stuff 8', 0, NULL)
) as t([id],[title],[description],[complete],[DependsOnTask])
So task 1 has 2 child tasks - 3 and 4. The 4th task got 1 child - 7. You want to get all child tasks of task with id = 1:
DECLARE #taskid int = 1
;WITH cte AS (
SELECT [id]
,[title]
,[description]
,[complete]
,[DependsOnTask]
FROM [ToDoItems]
WHERE [id] = #taskid
UNION ALL
SELECT t.*
FROM [ToDoItems] t
INNER JOIN cte c
ON c.id = t.DependsOnTask
)
SELECT *
FROM cte
Output:
id title description complete DependsOnTask
1 Title1 Do some stuff 1 0 NULL
3 Title3 Do some stuff 3 1 1
4 Title4 Do some stuff 4 1 1
7 Title7 Do some stuff 7 0 4
So if you change last select to:
SELECT #taskid as main,
id,
DependsOnTask
FROM cte
You will get:
main id DependsOnTask
1 1 NULL
1 3 1
1 4 1
1 7 4
So you get all child tasks of Task1.
If you change CTE like this:
;WITH cte AS (
SELECT [id]
,[title]
,[description]
,[complete]
,[DependsOnTask]
,[id] as Parent
FROM [ToDoItems]
WHERE [DependsOnTask] IS NULL
UNION ALL
SELECT t.*,
c.Parent
FROM [ToDoItems] t
INNER JOIN cte c
ON c.id = t.DependsOnTask
)
SELECT Parent,
id,
DependsOnTask
FROM cte
You will got all you need: Parent task, Child tasks and what are they dependent on:
Parent id DependsOnTask
1 1 NULL
2 2 NULL
8 8 NULL
2 5 2
2 6 2
1 3 1
1 4 1
1 7 4

Retrieving specific data from SQL server Stored procedure

The two tables I am using are
Contacts (Contact_id, Contact_name,...)
Contacts_group_options (Contact_id, Group_id, Status)
I want to get all the contacts that are not part of a specific group from the Contacts_group_options table .
The problem is, if first table has Contact_ids 1, 2, 3, 4 and second table has Contact_ids 2, 3 for a Group_id = 1, I want result as 1 and 4. But I am getting 1, 3, 4, 1, 2, 4.
First 1 is compared with 2. Both are not same. Output 1, then 2 with 2 same so don't output, 3 with 2 output 3, 4 with 2 output 4,1 with 3 output 1,2 with 3, output 2,3 with 3 dont output,4 with 3 output 4.
stored procedure is:
Select
m.Contact_id,
m.Contact_code,
m.Contact_name,
m.Phone_number,
m.Mail_id,
m.Designation,
m.Department,
m.User_id,
m.User_type,
m.Status
from
Contacts as m,
Contact_group_options as b
,#tblType_contacts2group as i
where
b.Group_id = i.Group_id
and m.Contact_id != b.Contact_id
I think you can do this with a simple NOT EXISTS:
SELECT c.*
FROM Contacts AS c
WHERE
NOT EXISTS(
SELECT 1
FROM Contact_group_options
WHERE
Group_id = 1
AND Contact_id = c.Contact_id
)
You can try this:
-- NOT EXISTS
SELECT C.*
FROM Contacts C
WHERE NOT EXISTS (SELECT 1 FROM Contacts_group_options WHERE Group_id = 1 AND C.Contact_id)
-- NOT IN
SELECT *
FROM Contacts
WHERE Contact_id NOT IN (SELECT Contact_id FROM Contacts_group_options WHERE Group_id = 1)
Your result set includes so many rows because you actually have three tables there: Contacts, Contact_group_options and #tblType_contacts2group.
Maybe you wanted to write something like this:
-- compacted for to make it more readable
Select
m.Contact_id, m.Contact_code, m.Contact_name, m.Phone_number,
m.Mail_id, m.Designation, m.Department, m.User_id, m.User_type, m.Status
from Contacts as m
where not exists (select 1 from #tblType_contacts2group as i where i.Group_id = m.Group_id)
NOTE: Since #tblType_contacts2group is not clearly defined in your example, above query might not get the correct results.
As already Felix mentioned, you should always use proper joins. E.g. the above query could be like this:
Select
m.Contact_id, m.Contact_code, m.Contact_name, m.Phone_number,
m.Mail_id, m.Designation, m.Department, m.User_id, m.User_type, m.Status
from Contacts as m
left join #tblType_contacts2group i on i.Group_id = m.Group_id
where i.Group_id IS NULL -- any non-null column from i can be used here
However, keep in mind that, usually, LEFT JOIN with IS NULL is slower that NOT EXISTS equivalent.

Resources