How to vertically join the same table - sql-server-2008

So I have a table like this:
DECLARE #Nodes TABLE (ID INT, Name VARCHAR(100), ParentID INT)
INSERT INTO #Nodes VALUES
(1, 'Test', NULL),
(2, 'Test', 1),
(3, 'Test', 2),
(5, 'Test', 3),
(6, 'Test', 1)
Now I would like to query that table, retrieving node with ID 3 and all parent nodes. ParentID would be a foreign key to the same table's ID column. The number of possible parent nodes is infinite. Result would be this:
ID Name ParentID
1 Test NULL
2 Test 1
3 Test 2
What is the best way to do this?

You can use a recursive common table expression:
WITH Nodes
AS(
SELECT 1 AS relationLevel, child.*
FROM #Nodes child
WHERE child.ID = 3
UNION ALL
SELECT relationLevel+1, parent.*
FROM Nodes nextOne
INNER JOIN #Nodes parent ON parent.ID = nextOne.ParentID
)
SELECT * FROM Nodes order by ID
DEMO

DECLARE #ID INT=3
;with cte as(
select ID,Name,ParentID FROM #Nodes WHERE ID=#ID
UNION ALL
SELECT n.ID,n.Name,n.ParentID FROM cte inner join #Nodes n on cte.ParentID= n.ID
)
SELECT ID,Name,ParentID FROM cte
ORDER BY ParentID

Related

SQL multi table query

I have three relations: Recipes(name, descr), Ingredients(recipeName, num, val) and Steps(recipeName, num, val). Each recipe will have one or more ingredients, and one or more steps. I am trying to write a query that will list the ingredients and steps for a single recipe. With this query
SELECT Recipes.name, Recipes.descr, Ingredients.num, Ingredients.val, Steps.num, Steps.val
FROM Recipes
LEFT OUTER JOIN Ingredients ON Recipes.id = Ingredients.recipeName
LEFT OUTER JOIN Steps ON Recipes.id = Steps.recipeName;
This query is like a cross product. So if my ingredients values for some recipe are 1,ing-a and 2,ing-b and steps values are 1,step-c, 2,step-d and 3,step-e, I get (order is Recipe.name, recipe.descr, Ingredients.num, Ingredients.val, Steps.num, Steps.val)
name, descr, 1, ing-a, 1, step-c
name, descr, 1, ing-a, 2, step-d
name, descr, 1, ing-a, 3, step-e
name, descr, 2, ing-b, 1, step-c
name, descr, 2, ing-b, 2, step-d
name, descr, 2, ing-b, 3, step-e
Is there a query that would return as shown below (recipe vals, then ingredients, then steps)
name
descr
1,ing-a
2,ing-b
1,step-c
2,step-d
3,step-e
Data
DROP TABLE iF EXISTS Recipes;
DROP TABLE iF EXISTS Ingredients;
DROP TABLE iF EXISTS Steps;
CREATE TABLE Recipes
(id INT,
name VARCHAR(100),
descr VARCHAR(100));
CREATE TABLE Ingredients
(recipeName INT,
num INT,
val VARCHAR(100));
CREATE TABLE Steps
(recipeName INT,
num INT,
val VARCHAR(100));
INSERT INTO Recipes VALUES (1, 'name', 'descr');
INSERT INTO Ingredients VALUES (1, 1, 'ing-a');
INSERT INTO Ingredients VALUES (1, 2, 'ing-b');
INSERT INTO Ingredients VALUES (1, 3, 'ing-c');
INSERT INTO Steps VALUES (1, 1, 'step-a');
INSERT INTO Steps VALUES (1, 2, 'step-b');
INSERT INTO Steps VALUES (1, 3, 'step-c');
Solution
SELECT v1.display
FROM ( SELECT Recipes.id AS sort_key, 1 AS seq, Recipes.name AS display
FROM Recipes
UNION ALL
SELECT Recipes.id AS sort_key, 2 AS seq, Recipes.descr AS display
FROM Recipes
UNION ALL
SELECT Ingredients.recipeName AS sort_key, 3 AS seq, CONCAT(Ingredients.num , ',' , Ingredients.val) AS display
FROM Ingredients
UNION ALL
SELECT Steps.recipeName AS sort_key, 4 AS seq, CONCAT(Steps.num , ',' , Steps.val) AS display
FROM Steps ) v1
ORDER BY v1.sort_key, v1.seq, v1.display;
Output
***display***
name
descr
1,ing-a
2,ing-b
3,ing-c
1,step-a
2,step-b
3,step-c
I just saw the question changed The following might not be valid.
You can use group concat to do this, like this:
SELECT
Recipes.name, Recipes.descr,
GROUP_CONCAT(CONCAT(Ingredients.num,' ',Ingredients.val) SEPARATOR ', ') AS Ingredients,
GROUP_CONCAT(CONCAT(Steps.num,' ',Steps.val) SEPARATOR ', ') AS Steps
FROM Recipes
LEFT OUTER JOIN Ingredients ON Recipes.id = Ingredients.recipeName
LEFT OUTER JOIN Steps ON Recipes.id = Steps.recipeName;
GROUP BY Recipes.name, Recipes.descr

Query to select a object with two exact records

I have following table in MySQL:
some_table
user_id | obj_id
-----------------
5 | 1
6 | 1
7 | 2
8 | 2
Now I need a Query, which will get me the obj_id, if this obj_id has both user_id = 5 and user_id = 6 (in the example above it is obj_id=1).
Is something like this possible with MySQL?
One method uses group by and having:
select obj_id
from some_table t
where user_id in (5, 6)
group by obj_id
having count(distinct user_id) = 2;
As #GordonLinoff already provided a better solution.
But this is one of the possible way to get your expected result using INTERSECT:
SELECT DISTINCT obj_id FROM #TEST WHERE user_id = 5
INTERSECT
SELECT DISTINCT obj_id FROM #TEST WHERE user_id = 6
Sample execution:
CREATE TABLE some_table (`user_id` INT, obj_id INT);
INSERT INTO some_table (`user_id`, obj_id) VALUES
(5, 1),
(6, 1),
(7, 2),
(8, 2),
(5, 3),
(6, 4);
SELECT DISTINCT obj_id FROM some_table WHERE `user_id` = 5
INTERSECT
SELECT DISTINCT obj_id FROM some_table WHERE `user_id` = 6
Result will be 1.

How to find the column having duplicate value in SQL Server

I have a table:
create table #t
(
ID int,
value nvarchar(5)
)
insert #t
values (1,'A'), (2, 'B'), (3, 'A'), (3, 'B')
Sample data:
ID value
------------
1 A
2 B
3 A
3 B
For my project I need the ID which has having both the values
Result :
ID
3
Kindly help me out.
To get IDs having 2 values
select id
from #t
group by id
having count(distinct value) >= 2
or to get all IDs having A and B
select id
from #t
where value in ('A','B')
group by id
having count(distinct value) = 2
or to make it more generic to get IDs having all values
select id
from #t
group by id
having count(distinct value) = (select count(distinct value) from #t)

Select primary photo if exist else pick first uploaded photo

I am trying to retrieve a single picture set by the user as the primary picture from table as below:
SELECT p.*, ph.* FROM place AS p
INNER JOIN photo as ph
ON p.place_id = ph.place_id
WHERE ph.primary_pic = 'X';
But not all user has set their primary picture, resulting in the query does not return anything.
IF(query is empty)
//perform SQL again with primary_pic = ''
Is there any ways or syntax that could be use to query this with one single SQL statement?
I was working in MS SQL but I don't see anything MS-specific except for the table variables. So if you change that to existing tables, it should run on MySQL too. (I am not sure but I guess MySQL should have EXCEPT set operation.)
-- sample data start
declare #place as table (plid int, plname nvarchar(100))
declare #photo as table (phid int, phname nvarchar(100), plid int, primary_pic nvarchar(1))
insert into #place values (1, 'aaa')
insert into #place values (2, 'bbb')
insert into #photo values (1, 'aaa_1.jpg', 1, '')
insert into #photo values (2, 'aaa_2.jpg', 1, 'X')
insert into #photo values (3, 'aaa_3.jpg', 1, '')
insert into #photo values (4, 'aaa_4.jpg', 1, '')
insert into #photo values (5, 'bbb_1.jpg', 2, '')
insert into #photo values (6, 'bbb_2.jpg', 2, '')
insert into #photo values (7, 'bbb_3.jpg', 2, '')
insert into #photo values (8, 'bbb_4.jpg', 2, '')
-- sample data end
-- note: #place and #photo are table variables in MS SQL
select p.*, ph2.*
from #place p inner join #photo ph2 on p.plid = ph2.plid
inner join (
select ph.plid, ph.primary_pic, min(ph.phid) phid
from #photo ph inner join
(select distinct plid from #photo where primary_pic <> 'X'
except
select distinct plid from #photo where primary_pic = 'X') hasnoprimary
on hasnoprimary.plid = ph.plid
group by ph.plid, ph.primary_pic
union
select ph.plid, ph.primary_pic, min(ph.phid) phid
from #photo ph inner join
(select distinct plid from #photo where primary_pic = 'X') hasprimary
on hasprimary.plid = ph.plid
where primary_pic = 'X'
group by ph.plid, ph.primary_pic
) trickypart on trickypart.phid = ph2.phid
Method: (1) get two lists of place IDs. One for which there is a primary photo and another for which there isn't (this is where I used EXCEPT). (2) join the photos table to both of them separately to get the photo IDs. For the first list, it is what was marked with X, for the second it is the minimum of all photo IDs. (3) make a union of the two. (4) join it back to places and photos.
In MS SQL it works ang gives the following for the sample data above:
plid plname phid phname plid primary_pic
----------- -------------- ----------- -------------- ----------- -----------
1 aaa 2 aaa_2.jpg 1 X
2 bbb 5 bbb_1.jpg 2

Is it possible to group rows twice in MySQL?

I have a table like this:
someid somestring
1 Hello
1 World
1 Blah
2 World
2 TestA
2 TestB
...
Currently I'm grouping by the id and concatenating the strings, so I end up with this:
1 Hello,World,Blah
2 World,TestA,TestB
...
Is it possible to do a second grouping so that if there are multiple entries that end up with the same string, I can group those too?
Yes, just put your current query in an inner select and apply a new GROUP BY to the outer select. Note that you will probably want to use ORDER BY of GROUP_CONCAT to ensure that the strings are always concatenated in the same order.
SELECT somelist, COUNT(*) FROM
(
SELECT
someid,
GROUP_CONCAT(somestring ORDER BY somestring) AS somelist
FROM table1
GROUP BY someid
) AS T1
GROUP BY somelist
Result:
'Blah,Hello,World', 1
'TestA,TestB,World', 2
Here's the test data I used:
CREATE TABLE table1 (someid INT NOT NULL, somestring NVARCHAR(100) NOT NULL);
INSERT INTO table1 (someid, somestring) VALUES
(1, 'Hello'),
(1, 'World'),
(1, 'Blah'),
(2, 'World'),
(2, 'TestA'),
(2, 'TestB'),
(3, 'World'),
(3, 'TestB'),
(3, 'TestA');