I need to show the location of content within a course that lives in a Virtual Learning Environment (VLE) - basically it is a teaching and learning website that is backed by a Postgres database.
I created the following query that returns the raw data. A sample of this is below
WITH RECURSIVE folders AS(
SELECT ccs.pk1, ccs.parent_pk1, ccs.position, ccs.folder_ind, ccs.title
FROM course_contents ccs
INNER JOIN course_main cm ON cm.pk1 = ccs.crsmain_pk1
WHERE cm.course_id = '<COURSE ID>'
and ccs.pk1 = '<INDEX>' -- primary key
UNION ALL
SELECT cc.pk1, cc.parent_pk1, cc.position, cc.folder_ind, cc.title
FROM course_contents cc
JOIN folders ON folders.pk1 = cc.parent_pk1
)
SELECT f.*
FROM folders f
SAMPLE DATA
PK1
PARENT_PK1
POSITION
FOLDER
TITLE
11497702
NULL
0
Y
Assessment
11497708
11497702
0
N
Using the Assessment Tools
11497709
11497702
1
N
Past Exams Papers
11497710
11497702
2
N
Using the Assessment Tools
11497711
11497702
3
N
Past Exams Papers
I would like to display it like the table below. Something to note is that there are multiple levels - files in folders, and folders in folders, etc.
PATH
Assessment - Using the Assessment Tools
Assessment - Past Exams Papers
Assessment - Using the Assessment Tools
Assessment - Past Exams Papers
I've used the following line
SELECT STRING_AGG(f.title,' - ') AS path
FROM folders f
GROUP BY f.parent_pk1
but it displays like
PATH
Assessment
Using the Assessment Tools - Past Exams Papers - Using the Assessment Tools - Past Exams Papers"
Any help would be very much appreciated.
Thanks
Hmmm . . . I think you want something like this rather than aggregation:
select f.*
from (select first_value(f.title) over (order by position) || '-' || first_value(f.title) over (order by position desc)
from folders f
) f
where folder = 'N';
Thanks to Gordon's answer above I have found the answer I was looking for.
I made a few changes to Gordon's code. Please see below. I do need to test this with more data though to see if it fully replicates the content structure.
select f.*
from (select first_value(f.title) over (order by position) || ' > ' ||
f.title
from folders f
where f.folder_ind = 'N'
) f
Related
Considering the table with the following schema, I'm trying to create a dynamic query that accepts a logical expression with multiple variables, so I could filter employees that have skills only with specific technology or category names.
For example, I'm inputing pseudo expression (technology = 'aws cognito' OR technology = 'azure active directory') AND category = 'framework', so I know that the first two variables are technologies and the last one is a category of technologies.
How the possible query could look like, considering that the number of variables can differ and overall the expression may be multi-leveled? The way that the query will be generated is another question, but I'm having troubles with the actual structure of it.
I've tried querying a very simple case like that (schema name is skills):
SELECT *
FROM "skills"."employees" "e"
LEFT JOIN "skills"."skills" "s" ON "s"."employee_id"="e"."id"
LEFT JOIN "skills"."technologies" "t" ON "t"."id"="s"."technology_id"
WHERE ("t"."name" = 'aws cognito'
AND "t"."name" = 'azure active directory')
However, the query does not return any entries, but I have employees in my database that have skills with both of these technologies, so I thought that the query will return them. It works with the OR operator, but it was expected. I have a very bad feeling that I'm doing something fundamentally wrong, but, to be honest, I just cannot wrap my head around it.
Update: Sample data
Employees
id first_name
1 Andrew
2 James
Technologies
id name
1 aws cognito
2 azure active directory
Skills
id employee_id technology_id
1 1 1
2 1 2
3 2 1
Based on the data and on the logical expression technology = 'aws cognito' AND technology = 'azure active directory' I'm expecting the result to be only the employee with id 1 (Andrew).
For an arbitrary set of requried skills you can use a grouping and counting method
with skillset(tname) as(
values
('aws cognito'),
('azure active directory')
)
SELECT e.id, e.first_name
FROM "skills"."employees" e
JOIN "skills"."skills" s ON s.employee_id=e.id
JOIN "skills"."technologies" t ON t.id=s.technology_id
JOIN skillset st ON t.name = st.tname
GROUP BY e.id, e.first_name
HAVING count(*) = (select count(*) from skillset)
enter image description here
I have two tables :
UserInfo
Skill
and the join table between them called UserSkill as you can see at the
right part of the diagram.
I want to know whoever knows or is skillful in Java, what else he is skillful at. I mean for example I know java, Go, PHP, python and user number 2 knows java and python and CSS. So the answer to the question: whoever knows java what else he knows would be GO, PHP, Python and CSS.
It's like recommendation systems for example whoever but this product what else do they bought? Like what we have in amazon ..
What would be the best query for this ?
Thank you
More information:
UserInfo
U-id U-name
1 A
2 B
3 C
SkillInfo
S-id S-Name
1 Java
2 GO
3 PHP
4 Python
5 CSS
UserSkill:
U-id S-id
1 1
1 2
1 3
1 4
2 1
2 4
2 5
In SQL Server 2017 and Azure SQL DB you can use the new graph database capabilities and the new MATCH clause to answer queries like this, eg
SELECT FORMATMESSAGE ( 'User %s has skill %s and other skill %s.', [user1].[U-name], mainSkill.[S-name], otherSkill.[S-name] ) result
FROM
dbo.users user1,
dbo.hasSkill hasSkill1, dbo.skills mainSkill, dbo.hasSkill hasSkill2, dbo.skills otherSkill
WHERE mainSkill.[S-name] = 'CSS'
AND otherSkill.[S-name] != mainSkill.[S-name]
AND MATCH ( mainSkill<-(hasSkill1)-user1-(hasSkill2)->otherSkill);
My results:
Obviously you can answer the same queries with a relational approach, it's just a different way of doing things. Full script available here.
To make this more dynamic, replace the hard coded 'java' with a variable that you can pass to filter by any skill type, possibly make a stored procedure so you can pass the variable,
Edited column names as I didn't look at the image you provided:
--Outer query selects all skills of users which are not java and user has skill of java,
--the inner query selects all user ids where the user has a skill of java
SELECT sk.[SkillName], ui.[UserName], ui.[UserId]
FROM [dbo].[Skill] AS sk
INNER JOIN [dbo].[UserSkill] AS us
ON us.[SkillId] = sk.[SkillId]
INNER JOIN [dbo].[UserInfo] AS ui
ON ui.[UserId] = us.[UserId]
WHERE sk.[Skill] <> 'java' AND ui.[UserId] IN (
SELECT [UserId]
FROM [dbo].[UserInfo] ui
INNER JOIN [dbo].[UserSkill] us
ON us.[UserId] = ui.[UserId]
INNER JOIN [dbo].[Skill] sk
ON sk.[SkillId] = us.[SkillId]
WHERE sk.[SkillName] = 'java')
This is what I have found
--Formatted query
select
o.UserName, k.SkillName
from
UserSkill S
inner join
UserSkill SS on s.UserID = ss.UserID
and s.SkillID = 1
and ss.SkillID <> 1
inner join
Skill k on k.SkillID = ss.SkillID
inner join
UsersINFO O on o.UserID = ss.UserID
There are two tables:
Table of peoples (m_id, m_name);
Table of links (m_id, f_id), where both fields link to m_id from first table
I need an Oracle database query that prints the word "Possible" if everyone is linked to everyone by not more than:
through 3 friends
through N friends
otherwise prints "Impossible"
Help me with this task if it's possible, or at least show me where to look for the answer, I mean what I have to read before, and what's necessary for solving this task.
I am not sure i got you question correct but i guess you require something like this.
select p.m_id,count(l.f_id),'Possible' col
from people p,
links l
where p.m_id = l.m_id
group by p.m_id
having count(l.f_id) >= 3
union
select p.m_id,count(l.f_id),'Impossible' col
from people p,
links l
where p.m_id = l.m_id
group by p.m_id
having count(l.f_id) < 3
I need your help building a SQL statement I can't wrap my head around.
In a database, I have four tables - files, folders, folders_files and links.
I have many files. One of them is called "myFile.txt".
I have many folders. "myFile.txt" is in some of them. The first folder it appears in is called "firstFolder".
I have many links to many folders. The first link to "firstFolder" is called "firstLink".
The data structure for the example would be:
// files
Id: 10
Name: "myFile.txt"
// folders
Id: 20
Name: "firstFolder"
// folder_files (join table)
Id: 30
Folder_Id: 20 (meaning "firstFolder")
File_Id: 1 (meaning "myFile.txt")
// links
Id: 40
Name: "firstLink"
Folder_Id: 20 (meaning "firstFolder")
FIRST QUESTION: How do I get the record for "myFile.txt" AND the Name and Id of "firstLink" (the first link), querying on file Id = 10, based on the lowest Id of the folder and the link?
SECOND QUESTION: How do I get the record for "myFile.txt" AND the Name and Id of "firstLink" (the first link), querying on all files, based on the lowest Id of the folder and the link?
put another way - how do I get the first link to the first folder containing "myFile.txt"?
Resulting in a record that looks like:
Id: 10
Name: "myFile.txt"
LinkId: 40
LinkName: "firstLink"
Thanks!
You should try to think about how you want your result set to look. SQL is designed to describe result sets. If you can write out a hypothetical result set, you might have an easier time writing SQL that will render that result set.
I had a hard time understanding what you are looking for, but I'm sure it's a fairly straight forward problem. I would be able to help you easier if you could describe you results more clearly, although you might not need my help anymore!
For example (going with you original schema) Q1 & Q2:
files.Id, files.Name, links.Id, links.Name (4 columns)
Q1:
SELECT
files.Id, files.Name, links.Id, links.Name
FROM
files, links
INNER JOIN
folder_files
ON files.Id = folder_files.File_Id
INNER JOIN
links
ON links.Id = folder_files.Folder_Id
WHERE
files.Id = 10
ORDER BY
folder_files.File_Id ASC, links.Id ASC
LIMIT 1;
(JOIN with folders table not necessary)
Q2:
Change both ASC to DESC
This selects all links for file id 10:
select links.id, links.name
from files
left join folder_files on files.id = folder_files.file_id
left join folders on folder_files.folder_id = folders.id
left join links on links.folder_id = folders.id
where files.id=10;
Change the where clause, add limit or whatever for other things you want. It should be simple to modify this.
I would try this:
select f.*
, l.Id as LinkId
, l.Name as LinkName,
from Link l
inner join Folder_Files ff on ff.Folder_Id = l.Folder_Id
inner join Files f on f.Id = ff.File_Id
where f.Id = 10
Resulting to:
Id | Name | LinkId | LinkName
10 | myFile.txt | 40 | firstLink
Is this what you want?
Taking into account:
more folders per file
more links per folder
taking the lowest id folder for link, and lowest id link for folder
With help of: mysql: group by ID, get highest priority per each ID
The answer for ALL files in the files table ( go for JohnB's solution for a single file, it would be faster):
SELECT file_id, file_name, link_id, link_name FROM (
SELECT file_id, file_name, link_id, link_name,
#r := CASE WHEN #prev_file_id = file_id
THEN #rn + 1
ELSE 1
END AS r,
#prev_file_id := file_id
FROM (
SELECT
f.id as file_id, f.name as file_name, l.id as link_id, l.name as link_name
FROM files f
JOIN folder_files ff
ON ff.file_id = f.id
JOIN links l
ON l.folder_id = ff.folder_id
ORDER BY ff.folder_id, l.id -- first folder first, first link to first folder second
) derived1,
(SELECT #prev_file_id := NULL,#r:=0) vars
) derived2
WHERE r = 1;
I have written a psychological testing application, in which the user is presented with a list of words, and s/he has to choose ten words which very much describe himself, then choose words which partially describe himself, and words which do not describe himself. The application itself works fine, but I was interested in exploring the meta-data possibilities: which words have been most frequently chosen in the first category, and which words have never been chosen in the first category. The first query was not a problem, but the second (which words have never been chosen) leaves me stumped.
The table structure is as follows:
table words: id, name
table choices: pid (person id), wid (word id), class (value between 1-6)
Presumably the answer involves a left join between words and choices, but there has to be a modifying statement - where choices.class = 1 - and this is causing me problems. Writing something like
select words.name
from words left join choices
on words.id = choices.wid
where choices.class = 1
and choices.pid = null
causes the database manager to go on a long trip to nowhere. I am using Delphi 7 and Firebird 1.5.
TIA,
No'am
Maybe this is a bit faster:
SELECT w.name
FROM words w
WHERE NOT EXISTS
(SELECT 1
FROM choices c
WHERE c.class = 1 and c.wid = w.id)
Something like that should do the trick:
SELECT name
FROM words
WHERE id NOT IN
(SELECT DISTINCT wid -- DISTINCT is actually redundant
FROM choices
WHERE class == 1)
SELECT words.name
FROM
words
LEFT JOIN choices ON words.id = choices.wid AND choices.class = 1
WHERE choices.pid IS NULL
Make sure you have an index on choices (class, wid).