I know similiar questions have been asked and answered before, I have reviewed them but still can't quite wrap my head around how to do this in my case.
I would like to create a query (I use postgreSQL) that would return users from my database filtered by name, sorted by the number of friends in common with a given user (the user sending the request).
The data structure is as follows:
I have a users table, that has a column called search_full_name which stores name + surname in the format of "ADAM SMITH". This is what I filter with.
I have a user_friends table that stores information about who is friends with whom. So I have two columns in there: user_id and friend_id . The data is symmetric, i.e. for every (1,3) there is a (3,1) entry.
So far in the friend search I was just using a query like
select * from users where users.search_full_name like '%query%'
But now, I would like to additionally order the result by the amount of friends in common with the user asking, so my query would have two inputs: query and userId.
Turns out I am not as good with sql as I thought, and I would really appreciate your help, it would be great to see some explanations too.
I imagine the desired output as:
+---------+------------------+----------------------+--+
| user_id | search_full_name | common_friends_count | |
+---------+------------------+----------------------+--+
| 45 | Adam Smith | 14 | |
| 123 | Adam Cole | 11 | |
| 12 | Adamic Kapi | 0 | |
+---------+------------------+----------------------+--+
for a query like 'Adam'
I have been trying this for a whole day now and I feel my brain has exploded.
Please help, thanks
The basic idea is a self-join. The following gets a match on users who share friends with the specified user:
select uf2.user_id, count(*) as num_friends
from user_friends uf join
user_friends uf2
on uf2.friend_id = uf.friend_id and
uf2.user_id <> uf2.user_id
where uf2.user_id = ?
group by uf2.user_id
order by count(*) desc; -- the user you care about
Ok, so after a few hours I came up with a query that works :) Here it is for future reference:
select u.id, u.search_full_name, count(uf.friend_id) as common_friend_count
from users u left join user_friends uf on (u.id = uf.user_id and uf.friend_id in (select friend_id from user_friends where user_id = ?))
where u.search_full_name like ?
group by u.search_full_name, u.id
order by common_friend_count desc;
I am working on a school assignment that has downright stumped me for days. The task is to, using a view (VAvailableGolfers), populate a list box with Golfers who are not tied to a given event/year selected from a combo box. Here is the data in the tables:
The expected output on the form, then, would be:
2015 shows Goldstein available
2016 shows no one available
2017 shows both Goldstein and Everett available
so, in other words, where there isn't a record in TGolferEventYears for a golfer for a particular year
I have tried left joins, full outer joins, exists, not in, not exists, etc and I cannot seem to nail down the SQL to make it happen.
Here is the VB Form and the SQL backing it. I cannot figure out what to code in the view:
"SELECT intGolferID, strLastName FROM vAvailableGolfers WHERE intEventYearID = " & cboEvents.SelectedValue.ToString
Here is the view, which I know isn't giving correct output:
select tg.intGolferID, strLastName, intEventYearID
from TGolferEventYears TGEY, TGolfers TG
Where tgey.intGolferID = tg.intGolferID
and intEventYearID not IN
(select intEventYearID
from TEventYears
where intEventYearID not in
(select intEventYearID
from TGolferEventYears))
Appreciate any help
I usually approach this type of question by using a cross join to generate all possibly combination and then a left join/where to filter out the ones that already exist:
select g.intGolferID, g.strLastName, ey.intEventYearID
from TEventYears ey cross join
TGolfers g left join
TGolferEventYears gey
on gey.intGolferID = g.intGolferID and
gey.intEventYearID = ey.intEventYearID
where gey.intGolferID is null;
Try this query:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
FROM TGolfers tg, TEventYears tey
WHERE tg.intGolferID NOT IN (
SELECT DISTINCT tgey.intGolferID
FROM TGolferEventYears tgey
WHERE tgey.intEventYearID = tey.intEventYearID
)
Explanation
Since you are trying to get combinations of data that is not in TGolferEventYears, you cannot use it in your outer-most SELECT as any of its columns would be NULL. Therefore, you need to SELECT FROM the tables that are the sources of that data, and going through each joined record, filter out the combinations that are in TGolferEventYears.
Main query
Select the data you need:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
...from TGolfers, cross join with TEventYears:
FROM TGolfers tg, TEventYears tey
...where the golfer ID does not exist in the following collection:
WHERE tg.intGolferID NOT IN ( ... )
Subquery
Select unique golfer IDs:
SELECT DISTINCT tgey.intGolferID
...from TGolferEventYears:
FROM TGolferEventYears tgey
...where the year is the current year of the outer query:
WHERE tgey.intEventYearID = tey.intEventYearID
Result
+-------------+-------------+----------------+--------------+
| intGolferID | strLastName | intEventYearID | intEventYear |
+-------------+-------------+----------------+--------------+
| 1 | Goldstein | 1 | 2015 |
| 1 | Goldstein | 3 | 2017 |
| 2 | Everett | 3 | 2017 |
+-------------+-------------+----------------+--------------+
The table I'm working on is a Employee log-in database. Amongst other things, it will be able to identify the time of entrance from an employee and to make a report on how his entrances have been during a set of dates.
The problem I am facing right now is: when the boss wants to see how a worker's log-in and log out work during a specific date. He wants to:
a) set a date and a name, and for the query to return the accesses from that employee on that specific date.
b) IF he inputs no name, then it will return a list with everyone for that date.
No | Name | ID | Date | Log-in | Log-out | Description
---+------+-----+------------+--------+---------+---------------------
1 | Joe1 | j1 | 2018/10/02 | 09:00 | 18:00 | Normal Exit
2 | Joe2 | j2 | 2018/10/02 | 10:00 | 17:00 | Meeting
.. | ... | . | . . . | . . | . . . | . . . .
N | JoeN | jN | yyyy/mm/dd | hh/mm | hh/mm | Whatever text
So for the first part I went with a simple
select *
from DB
where #name = '*the name of the person to look for*'
and #date = '*specific date*' (is a date variable yyyy-mm-dd)
all done. Now the next part seems weird and have not been able to figure it out yet.
I was trying with an IIF scenario as a subquery but doesn't do the part where it returns the complete list.
Next is the where case but cant make it work either. I am missing a variable for the program to check:
if there is no name to look for => show table with everyone for that day
if there is a name to look for => show table ONLY for that person
How can I make it so my program discriminates which table to show at the end?
Cheers!
EDIT:
ADDED (thanks for the recomendation) query for better assistance
CREATE TABLE DailyRegisters
(
No INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
ID VARCHAR(50) NOT NULL,
Date DATE NOT NULL,
Login DATETIME NOT NULL,
Logout DATETIME NOT NULL,
Description VARCHAR(50) NOT NULL
);
INSERT INTO DailyRegisters (No, Name, ID, Date, Login, Logout, Description)
VALUES (1,'Person 1','P1','10/2/2018','9:00','18:00','Normal'),
(2,'Person 2','P2','10/2/2018','9:01','18:00','Normal'),
(3,'Person 3','P3','10/2/2018','10:17','16:00','Plant'),
(4,'Person 4','P4','10/2/2018','10:10','18:00','Normal'),
(5,'Person 1','P1','10/3/2018','9:01','18:00','Normal'),
(6,'Person 2','P2','10/3/2018','9:02','18:00','Normal'),
(7,'Person 3','P3','10/3/2018','9:04','18:00','Normal'),
(8,'Person 1','P1','10/4/2018','9:00','18:00','Normal'),
(9,'Person 2','P2','10/4/2018','9:00','14:00','Meeting'),
(10,'Person 3','P3','10/4/2018','9:17','18:00','Normal'),
(11,'Person 4','P4','10/4/2018','9:00','18:00','Normal'),
(12,'Person 1','P1','10/5/2018','9:00','18:00','Normal'),
(13,'Person 3','P3','10/5/2018','9:00','20:00','Plant'),
(14,'Person 4','P4','10/5/2018','9:00','18:00','Normal');
You simply need to handle the case wherein no name is specified, like this:
select * from DB
where
((#name is not null AND name = #name) or (#name is null))
AND date = #date;
Basically the above query will not check for name if the #name parameter is null. And of course it goes without saying that #name and #date are parameters passed to the query.
I also suggest not to use select * but instead specify the columns: It is better for maintenance in case your table schema changes.
I'm currently trying to find the 3 most popular articles in a database. I want to print out the title and amount of views for each. I know I'll have to join two of the tables together (articles & log) in order to do so.
The articles table has a column of the titles, and one with a slug for the title.
The log table has a column of the paths in the format of /article/'slug'.
How would I join these two tables, filter out the path to compare to the slug column of the articles table, and use count to display the number of times it was viewed?
The correct query used was:
SELECT title, count(*) as views
FROM articles a, log l
WHERE a.slug=substring(l.path, 10)
GROUP BY title
ORDER BY views DESC
LIMIT 3;
If I understood you correctly you just need to join two tables based on one column using aggregation. The catch is that you can't compare them directly but have to use some string functions before.
Assuming a schema like this:
article
| title | slug |
-------------------
| title1 | myslug |
| title2 | myslug |
log
| path |
--------------------------
| /article/'myslug' |
| /article/'unmentioned' |
Try out something like the following:
select title, count(*) from article a join log l where concat('''', a.slug, '''') = substring(l.path, 10) group by title;
For more complex queries it can be helpful to at first write smaller queries which help you to figure out the whole query later. For example just check if the string functions return what you expect:
select substring(l.path, 10) from log l;
select concat('''', a.slug, '''') from article a;
Here's my problem: I have a query that joins multiple tables to show details of some orders. The query result in a table with columns:
order ID | name | count | price | location | date
It's a hospital database and what i want to do is to add another column that says how many patients were at that location at given date.
There's another table that shows patient stays - I need to count those.
patient ID | location | dateFrom | dateTo
The thing is that the STAYS table shows 2 dates - FROM and TO so I need to count every patient that was present at given location (ward) when order was placed.
Here's the initial query I need to update:
SELECT
AP_ZAMPOZ.ID_TOW AS IDTowaru, --merchandiseID
GMSL_TOW.NAZWA_TOW AS Nazwa, --name
GMSL_TOW.MNOZNIK_SYN AS Mnoznik, --quantity
AP_ZAMPOZ.ZAM_CENA_S AS Cena, --price
AP_ZAMPOZ.ZAM_IL AS Ilosc, --count
AP_ZAMNAG.ZAM_DATE AS DataZam, --date
GMSL_MAG.NAZWA_MAG AS Magazyn, --location
APSL_TOW_PROD.PROD_NAZWA AS Producent, --producer
APSL_TOW_ATC.NAZWA AS Grupa -group
FROM
AP_ZAMPOZ
JOIN
GMSL_TOW ON AP_ZAMPOZ.ID_TOW = GMSL_TOW.ID_TOW
JOIN
AP_ZAMNAG ON AP_ZAMNAG.ZAM_ID_NAG = AP_ZAMPOZ.ZAM_ID_NAG
JOIN
GMSL_MAG ON AP_ZAMNAG.ID_MAG = GMSL_MAG.ID_MAG
JOIN
APSL_TOW ON AP_ZAMPOZ.ID_TOW = APSL_TOW.ID_TOW
LEFT JOIN
APSL_TOW_PROD ON APSL_TOW.ID_PROD = APSL_TOW_PROD.ID_PROD
LEFT JOIN
APSL_TOW_ATC ON APSL_TOW.KOD = APSL_TOW_ATC.KOD
The table with stays is called POBYT and has these relevant columns:
| ID_POB (ID) | IDK_JOS (location identifier) | DT_OD (date From) | DT_TO (date To)
Rows that I would like to see should look like those in my present query + number of patients at given location at given date.
Anyone have any ideas how to achieve this? I'm stuck...
problem solved by adding subquery as another SELECT column.
Heres the code
SELECT
.
.
.
APSL_TOW_ATC.NAZWA AS Grupa
(SELECT Count(*)
FROM pobyt
WHERE (TO_DATE( AP_ZAMNAG.ZAM_DATE, 'YY/MM/DD') >= TO_DATE(DT_OD, 'YY-MM-DD') AND (TO_DATE( AP_ZAMNAG.ZAM_DATE, 'YY/MM/DD') <= TO_DATE(DT_DO, 'YY-MM-DD') OR dt_do IS NULL))
AND IDK_JOS = GMSL_MAG.KOD_MAG) AS LiczbaPacjentow --no. of patients at given date
FROM AP_ZAMPOZ
.
.
.
Works great.