Query for a threaded discussion - sql-server-2008

I have a single table with a self reference InReplyTo with some data like this:
PostID InReplyTo Depth
------ --------- -----
1 null 0
2 1 1
3 1 1
4 2 2
5 3 2
6 4 3
7 1 1
8 5 3
9 2 2
I want to write a query that will return this data in it's threaded form so that the post with ID=2 and all it's descendants will output before PostID=3 and so on for unlimited depth
PostID InReplyTo Depth
------ --------- -----
1 null 0
2 1 1
4 2 2
6 4 3
9 2 2
3 1 1
5 3 2
8 5 3
7 1 1
Is there a simple way to achieve this? I am able to modify the DB structure at this stage so would the new hierarchy datatype be the easiest way to go? Or perhaps a recursive CTE?

-- Test table
declare #T table (PostID int, InReplyTo int, Depth int)
insert into #T values (1, null, 0), (2, 1, 1), (3, 1, 1), (4, 2, 2),
(5, 3, 2), (6, 4, 3), (7, 1, 1), (8, 5, 3),(9, 2, 2)
-- The post to get the hierarchy from
declare #PostID int = 1
-- Recursive cte that builds a string to use in order by
;with cte as
(
select T.PostID,
T.InReplyTo,
T.Depth,
right('0000000000'+cast(T.PostID as varchar(max)), 10)+'/' as Sort
from #T as T
where T.PostID = #PostID
union all
select T.PostID,
T.InReplyTo,
T.Depth,
C.Sort+right('0000000000'+cast(T.PostID as varchar(max)), 10)+'/'
from #T as T
inner join cte as C
on T.InReplyTo = C.PostID
)
select PostID,
InReplyTo,
Depth,
Sort
from cte
order by Sort
Result:
PostID InReplyTo Depth Sort
----------- ----------- ----------- --------------------------------------------
1 NULL 0 0000000001/
2 1 1 0000000001/0000000002/
4 2 2 0000000001/0000000002/0000000004/
6 4 3 0000000001/0000000002/0000000004/0000000006/
9 2 2 0000000001/0000000002/0000000009/
3 1 1 0000000001/0000000003/
5 3 2 0000000001/0000000003/0000000005/
8 5 3 0000000001/0000000003/0000000005/0000000008/
7 1 1 0000000001/0000000007/

What you are looking for is indeed a recursive query.
A matching example to your case can be found here

Related

SQL JSON functions

I'm getting a query output as follows from a table
RowId QuestionGroupId QuestionId Answer
1 2 1 Single
2 2 2 With Kids
3 2 3 Not At All
4 3 1 Single
5 3 2 With Kids
6 3 3 Occasionally Smoke
But the result I would Like to get is as something follows
RowId QuestionGroupId ValueSet
1 2 [{QuestionId:1,Answer:Single},QuestionId:2,Answer:WithKids},QuestionId:3,Answer:Nt at all}]
2 3 [{QuestionId:1,Answer:Single},QuestionId:2,Answer:WithKids},QuestionId:3,Answer:Occasionally Smoke}]
So how to I convert the rows of record to JSON using SQL JSON ability.
Using FOR JSON AUTO given me all in one row of record.Your help is appreciated.
I don't really get what you're doing with the RowId column, but you should be able to do something like this with a correlated subquery to get what you're after
;with src
(
RowId,
QuestionGroupId,
QuestionId,
Answer
) as
(
select 1, 2, 1, 'Single'
union all select 2, 2, 2, 'With Kids'
union all select 3, 2, 3, 'Not At All'
union all select 4, 3, 1, 'Single'
union all select 4, 3, 2, 'With Kids'
union all select 4, 3, 3, 'Occasionally Smoke'
)
select
QuestionGroupId,
(
select i.QuestionId, i.Answer
from src i
where o.QuestionGroupId = i.QuestionGroupId
order by QuestionId
for json auto
) as ValueSet
from src o
group by QuestionGroupId

Automatic sequence number (1,2, …) to be filled in a specific field

I have a view table as below and I want to make the custom field named “number” to have as automatic sequence number (1,2,….) to be filled in the column CUSTOMFIELDVALUE according to REQUESTID
enter image description here
I need a trigger code on the WOCUSTOMFIELD table that do what I want
Thanks in Advance
Lubna
The trigger grabs the max value from t and stores it in a table variable. The cte works out the row number of each insert to t and in the update phase adds it to the value stored int the table variable.
use sandbox
go
--drop table t
--create table t(workid int identity, requestid int,customfieldvalue int)
--go
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[tGenCustomid]'))
drop trigger tGenCustomid
go
CREATE TRIGGER tGenCustomid
ON t
after insert
AS
BEGIN
DECLARE #max TABLE (
workid int,
requestid int,
customfieldvalue int
);
INSERT INTO #max (requestid,customfieldvalue)
select requestid,
coalesce(max(customfieldvalue),0)
from t
group by requestid
;with cte as
(select i.workid,i.requestid, row_number() over (partition by i.requestid order by i.workid) rn,
m.customfieldvalue
from inserted i
join #max m on m.requestid = i.requestid
)
update t
set customfieldvalue = cte.rn + cte.customfieldvalue
from t
join cte on cte.workid = t.workid and cte.requestid = t.requestid
END;
go
SET NOCOUNT ON
truncate table debug_table
truncate table t
print 'First bunch of inserts'
insert into t(requestid, customfieldvalue)
values
(1,0),(1,0),
(2,0),(2,0),(2,0),
(3,0)
select * from t
print 'Second bunch of inserts'
insert into t(requestid, customfieldvalue)
values
(1,0),(1,0)
select * from t
print 'Third bunch of inserts'
insert into t(requestid, customfieldvalue)
values
(1,0),(4,0),(3,0)
select * from t
First bunch of inserts
workid requestid customfieldvalue
----------- ----------- ----------------
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 1
Second bunch of inserts
workid requestid customfieldvalue
----------- ----------- ----------------
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 1
7 1 3
8 1 4
Third bunch of inserts
workid requestid customfieldvalue
----------- ----------- ----------------
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 1
7 1 3
8 1 4
9 1 5
10 4 1
11 3 2

Need MySql query that should return result by ordering values in particular field

Need MySql query that should return result by ordering values in particular field.
From below my result set should contain order like parent_id (1,4,6) should come first parent_id(2,3,7) come next and other should come last.
d data parent_id
----------------------
1 a1 1
2 abc 3
3 abcd 4
4 xyz 2
5 zxyy 6
2 abc 8
3 abcd 9
4 xyz 2
5 zxyy 15
Use a CASE expression in your ORDER BY clause:
SELECT d, data, parent_id
FROM yourTable
ORDER BY CASE WHEN parent_id IN (1, 4, 6) THEN 1
WHEN parent_id IN (2, 3, 7) THEN 2
ELSE 3 END,
parent_id
Follow the link below for a running demo:
SQLFiddle
Use below mentioned query
SELECT d, data, parent_id FROM table_name ORDER BY FIELD(parent_id,1,4,6,2,3,7);

SQL - Return the order of products purchased by adding new column

I'm working on another SQL query.
I have the following table.
PURCHASES
ID CUST_ID PROD_CODE PURCH_DATE
1 1 'WER' 01/12/2012
2 2 'RRE' 02/10/2005
3 3 'RRY' 02/11/2011
4 3 'TTB' 15/05/2007
5 3 'GGD' 20/06/2016
6 2 'SSD' 02/10/2011
I'm trying to add another column PURCH_COUNT that would display the purchase count for the CUST_ID based on PURCH_DATE.
If this is a first purchase it would return 1, if second then 2, and so on.
So the result I'm hoping is:
ID CUST_ID PROD_CODE PURCH_DATE PURCH_COUNT
1 1 'WER' 01/12/2012 1
2 2 'RRE' 02/10/2005 1
3 3 'RRY' 02/11/2011 2
4 3 'TTB' 15/05/2007 1
5 3 'GGD' 20/06/2016 3
6 2 'SSD' 02/10/2011 2
Thanks in advance!
Sample Data
DECLARE #Table1 TABLE
(ID int, CUST_ID int, PROD_CODE varchar(7), PURCH_DATE datetime)
;
INSERT INTO #Table1
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
IN SQL :
select ID,
CUST_ID,
PROD_CODE,
PURCH_DATE,
ROW_NUMBER()OVER(PARTITION BY CUST_ID ORDER BY (SELECT NULL))RN
from #Table1
In MySql :
SELECT a.ID, a.CUST_ID,a.PROD_CODE,a.PURCH_DATE, (
SELECT count(*) from #Table1 b where a.CUST_ID >= b.CUST_ID AND a.ID = b.ID
) AS row_number FROM #Table1 a
Use a correlated sub-query to get the counts per customer.
SELECT t.*,
(SELECT 1+count(*)
FROM table1
WHERE t.cust_id = cust_id
AND t.purch_date > purch_date) as purch_cnt
FROM table1 t
ORDER BY cust_id,purch_date
SQL Fiddle
Any correlated subquery or window function can be expressed as a join, too. Sometimes a join is easier to understand, or is produced from components you can re-use, and sometimes the DBMS doesn't support the fancier feature. (AFAIK, a subquery in a SELECT clause is nonstandard.)
create table T
(ID, CUST_ID, PROD_CODE, PURCH_DATE);
INSERT INTO T
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
select PURCH_COUNT, T.*
from T join (
select count(b.ID) as PURCH_COUNT
, a.CUST_ID, a.PURCH_DATE
from T as a join T as b
on a.CUST_ID = b.CUST_ID
and b.PURCH_DATE <= a.PURCH_DATE
group by a.CUST_ID, a.PURCH_DATE
) as Q
on T.CUST_ID = Q.CUST_ID
and T.PURCH_DATE = Q.PURCH_DATE
;
Output of subquery:
PURCH_COUNT CUST_ID PURCH_DATE
----------- ---------- -------------------
1 1 2012-01-12 05:30:00
1 2 2005-02-10 05:30:00
2 2 2011-02-10 05:30:00
1 3 2008-03-05 05:30:00
2 3 2011-02-11 05:30:00
3 3 2017-08-06 05:30:00
Output of query:
PURCH_COUNT ID CUST_ID PROD_CODE PURCH_DATE
----------- ---------- ---------- ---------- -------------------
1 1 1 WER 2012-01-12 05:30:00
1 2 2 RRE 2005-02-10 05:30:00
2 3 3 RRY 2011-02-11 05:30:00
1 4 3 TTB 2008-03-05 05:30:00
3 5 3 GGD 2017-08-06 05:30:00
2 6 2 SSD 2011-02-10 05:30:00

MYSQL - calculating differences within a single table

I'm looking for a way to calculate differences between integers within a single table.
I'm planning a MYSQL table that looks like this:
user question answer
1 1 3
1 2 3
1 3 2
1 4 5
1 5 1
2 1 2
2 2 3
2 3 1
2 4 5
2 5 3
3 1 3
3 2 3
3 3 4
3 4 5
3 5 3
4 1 5
4 2 3
4 3 2
4 4 5
4 5 1
Each user (in this example) has answered 5 questions, giving an answer on a scale of 1 to 5.
What I'm looking to work out is which of the users 2, 3 and 4 have given answers that are most similar to those provided by user 1.
What I have in mind is calculating the difference between the answers given by each user for each question, in comparison to those of user 1, and then adding up those differences.
The user with the lowest number after that addition would be most similar to user 1.
I'm sorry to say that I don't really know where to begin constructing a query that does this efficiently and was wondering if anyone could point me in the right direction?
I'm also open to any suggestions for any better or more logical way to build the same results.
SELECT SUM(ABS(t2.answer - t1.answer)) AS total_diff, t2.user
FROM my_table AS t1
LEFT JOIN my_table AS t2 USING(question)
WHERE t1.user = 1 AND t2.user != t1.user
GROUP BY t2.user
ORDER BY total_diff ASC
result:
total_diff user
2 4
4 2
4 3
SELECT
yt1.user,
SUM(CASE WHEN yt1.answer = yt2.answer THEN 1 ELSE 0 END) AS howMuchAnswersInCommon
FROM yourTable yt1
INNER JOIN yourTable yt2 ON yt1.question = yt2.question
WHERE yt2.user = 1 AND yt1.user != 1
GROUP BY yt1.user
ORDER BY howMuchAnswersInCommon DESC
;
This will give you the one with the most common answers to user 1 on top.
Test data:
/*
create table yourTable (user int, question int, answer int);
insert into yourTable values
(1, 1, 3),
(1, 2, 3),
(1, 3, 2),
(1, 4, 5),
(1, 5, 1),
(2, 1, 2),
(2, 2, 3),
(2, 3, 1),
(2, 4, 5),
(2, 5, 3),
(3, 1, 3),
(3, 2, 3),
(3, 3, 4),
(3, 4, 5),
(3, 5, 3),
(4, 1, 5),
(4, 2, 3),
(4, 3, 2),
(4, 4, 5),
(4, 5, 1);
*/
OUTPUT:
user howMuchAnswersInCommon
4 4
3 3
2 2