I am really stuck on how to create the appropriate select statement and would appreciate any guidance you can offer.
I have created a mini document management system that allows users to upload files and to categorize those files by categories. Each file can have one or many categories selected. Following are my tables:
Table: files (pKey (primary key), file_name, file_description, file_path)
pKey file_name file_description file_path
1 IT001.DOC Network Design Document /common/it/
2 IT002.DOC Desktop Standards /common/it/
3 IT003.DOC Laptop Standards /common/it/
There are other departments in addition from IT so the path field also changes (Just thought I would toss that bit of datum in)
Table: categories (pKey (primary key), category_description)
pKey category_description
1 Central Missouri Campus
2 Eastern Missouri Campus
3 Western Missouri Campus
4 Desktops
5 Laptops
6 Networks
7 Printers
Of course there are other categories as well, this is just a sampling
Table: category_xref (pKey (primary key), fk_file_id, fk_category_id)
pKey fk_file_id fk_category_id
1 1 1
2 1 2
3 1 6
4 2 2
5 2 3
6 2 4
7 3 1
8 3 2
9 3 3
10 3 5
When the user searches for related documents they are presented a form with the Category checkboxes. By choosing Central, they get all files that have been marked as Central. By choosing Desktops, they get any documents that have been marked as Desktops. However, when they select Central AND Desktops they get any document that is either Central OR Desktops. I need to figure out how to get only those documents that are BOTH Central AND Desktops AND any other checkboxes they have selected, and exclude any that do not contain ALL the checkboxes selected.
SELECT f.pkID, f.file_name, f.file_description, f.file_path, cox.fk_category_id
FROM files f
JOIN category_xref cox ON cox.fk_file_id = f.pkID
WHERE cox.fk_category_id IN (59, 69)
ORDER BY f.file_name ASC, cox.fk_category_id ASC
The simplest way would probably be:
SELECT f.pkID, f.file_name, f.file_description, f.file_path
FROM files f
JOIN category_xref cox ON cox.fk_file_id = f.pkID
WHERE cox.fk_category_id IN (59, 69)
GROUP BY f.pkID
HAVING count(distinct cox.fk_category_id)=2
ORDER BY f.file_name ASC
You can try something in these lines:
SELECT f.pkID, f.file_name, f.file_description, f.file_path
FROM files f
RIGHT JOIN category_xref cox1 ON cox.fk_file_id = f.pkID AND cox1.fk_category_id = 59
RIGHT JOIN category_xref cox2 ON cox.fk_file_id = f.pkID AND cox2.fk_category_id = 69
ORDER BY f.file_name ASC
Related
I have a database which holds maintenance reports for unique units that I need to pull information from in a single query. My issue is the convoluted way the information is stored.
SELECT date(reports.created_timestamp) as Date,
locations.name as Location,
location_groups.name as L_group,
units.name as Unit_name,
units.id as Unit_ID,
concat(users_2.first_name, "
",users_2.last_name) as Engineer,
reports.id as Report_ID
FROM reports
LEFT JOIN units
ON reports.unit_id = units.id
LEFT JOIN users_2
ON reports.user_id = users_2.id
LEFT JOIN locations
ON units.location_id = locations.id
LEFT JOIN location_groups
ON locations.locations_group_id = location_groups.id
where reports.created_timestamp >= '2022-01-01 00:00:00'
and locations.name NOT LIKE 'Company Vehicles'
and location_groups.name = 'Airports';
This returns:
Date
Location
L_group
Unit_name
Unit_ID
Engineer
Report_ID
2022-10-18
Airport 1
Airports
AIR-AAA1-N127
1
Engineer 1
19471
2022-10-19
Airport 2
Airports
AIR-BBB2-D03
4
Engineer 2
19486
2022-10-19
Airport 2
Airports
AIR-BBB2-D14
13
Engineer 2
19495
2022-10-25
Airport 2
Airports
AIR-BBB2-D13
14
Engineer 2
19518
However, when the engineers are completing maintenance they have to do several tasks depending on the "unit" type. This is held in the units table as unit_type_id variable. This then references display_type_report_tasks to determine what tasks should be completed based on the unit_type_id, e.g:
id
unit_type_id
report_task_id
1
3
1
2
13
1
9
13
2
3
14
1
4
15
1
The report_task_id's then relate to table report_tasks which holds the following as an example:
id
name
text
description
1
Unit Cleaned-Internal
Unit Cleaned-Internal
Unit Cleaned-Internal
2
Unit Cleaned-External
Unit Cleaned-External
Unit Cleaned-External
3
Bumper Bar Fixed
Bumper Bar Fixed
Bumper Bar Fixed
4
Tile Replaced
Tile Replaced
Tile Replaced
5
Hardware Replaced
Hardware Replaced
Hardware Replaced
Picking unit type 13 as an example, when an engineer completes a report, the task options are stored in reports_tasks_selected_options such as the following:
id
report_tasks_option_id
example_report_id
30452
1
19558
30453
9
19558
The report_tasks_option_id then relates to report_tasks_options:
id
value
description
report_task_id
1
1
Not cleaned
1
2
2
Internal Clean
1
3
3
Deep Clean
1
7
1
Not cleaned
2
8
2
External Clean
2
9
3
Deep Clean
2
I want to query the unit_type_id and dynamically assign the column headers using report_tasks.name depending on what unit types are returned. (If a unit is returned that doesn't utilise all columns then NULL is used where no result is returned.)
I have to build reports per unit_type and statically assign the columns, BUT I cannot figure out how to then iterate through reports_tasks_selected_options to get the report_tasks_options.description for the results. As an example (using report_id = 19558) my current query output would be:
Date
Location
L_group
Unit_name
Unit_ID
Engineer
2022-11-01
Airport 3
Airports
AIR-CCC1-D09
1907
Engineer 3
But because I know this is unit_type=13 I know that the report_tasks related to it are Unit Cleaned-Internal and Unit Cleaned-External. So I want to iterate through the reports_tasks_selected_options table for that report number (As I know that the lower ID is always Unit Cleaned-Internal because it has the lower ID in report_tasks, and so the higher ID from reports_tasks_selected_options is always going to be Unit Cleaned-External.)
Desired output:
Date
Location
L_group
Unit_name
Unit_ID
Engineer
Report_ID
Clean-Int
Clean-Ext
2022-11-01
Airport 3
Airports
AIR-CCC1-D09
1907
Engineer 3
19558
Not cleaned (1)
Deep Clean (9)
I put the option numbers in brackets for clarity but do not require them in my output.
Folks can you please give your suggestions for my question regarding mysql joins.
My Table structures:
place table:
place_id place_name city
1 Hotel Golconda Hyderabad
2 Paradise Hotel Hyderabad
3 Hotel Mayuri Hyderabad
place_tags
tag_id tag_name
1 Valet Parking
2 Air Conditioned
3 Buffet
4 Bar
5 Family Dining
places_info Table:
place_id tag_id
1 1
1 2
1 3
2 1
2 5
3 1
3 4
The above is all my tables which are containing the place names and address in places table, all the facilities of the restaurants in tags table and mapping of the facilities of each place in places_info table.
Is this my table structures are correct to get the places which had "Valet parking and Buffet". How can write a join query for this type of results to get.
Most Importantly we had millions of places in places table and also in the places_info table. How to achieve maximum performance with this type of table structure? Or shall I need to change the table structures?
Please guide me.
This'd be the basic structure for "places with valet AND buffet":
SELECT place_id, COUNT(places_info) AS cnt
FROM place
LEFT JOIN places_info ON place.place_id = places_info.place_ID
AND tag_id IN (1, 3)
^^^^---- two tags: valet(1) + buffet(3)
GROUP BY place.place_id
HAVING cnt = 2
^^^---- must have both tags
For a places which have NEITHER of the tags, or only one, the count would come back 0, or 1, and get dumped by the HAVING clause.
I'm not so expert. I need to join 2 tables but except a specific rows whose status is 'Del Edge'. And also duplicate rows are not allowed. I also try to write an query but I think it's not in correct form. For example user 't' log in and he search a name 'Bush', so I need only those data. Any help would be appreciated. Here are the example tables:
links Table:
Id(PK) source target freq
1 Bush Fisher 1
2 Lamburt Bush 6
3 Sam Bush 3
4 Fisher Sam 7
5 Bush Dalai 4
logs Table:
username Id (FK) source target frequency status
t 5 Bush Dalai 4 Add Node
m 8 Dalai Pit 5 Del Edge
t 3 Sam Bush 3 Del Edge
Joining Table should be:
source target frequency
Bush Fisher 1
Lamburt Bush 6
Bush Dalai 4
My Query:
"SELECT source, target, frequency from links, logs
where (links.source=Bush || links.target= Bush) &&
where not exists
(SELECT source, target, frequency FROM logs
WHERE (links.id = logs.id && logs.status=Del Edge)";
The following should do the trick!
SELECT DISTINCT k.source,
k.target,
k.frequency
FROM links k
LEFT JOIN logs g
ON g.id = k.id
WHERE IFNULL(status, '') != 'Del Edge'
AND 'Bush' IN( k.source, k.target )
Hope this helps!
Also, the following fiddle demonstrates that the above answer is, in fact, correct: http://sqlfiddle.com/#!2/9753f/5
I've a tree structure, and its subsequent assignment table for customer categories in an sql server database.
CustomerCategory (CategoryID, ParentId)
CustomerInCategory(CustomerID, CategoryID)
If a CustomerCategory has any customer assigned to it, we can't add another subcategory to it. So, Customer can only be added to the lowest level in every sub tree. In other sense, the result of this query
SELECT * FROM `CustomerCategory` WHERE `CategoryId` NOT IN
(SELECT DISTINCT `parentid` FROM `CustomerCategory` WHERE `parentid` IS NOT NULL)
would yield leaf nodes. The Other thing is that, this tree might have subtrees of different levels, and we also, don't want to bound the number of levels in anyway, however, our users won't need more than 10 levels. Consider this as an illustration
CategoryID------ParentID---------------Name
1 NULL All Customers
2 1 Domestic
3 1 International
4 2 Independent Retailers
5 2 Chain Retailers
6 2 Whole Sellers
7 5 A-Mart
8 5 B-Mart
9 4 Grocery Stores
10 4 Restaurants
11 4 Cafes
CustomerID---------CustomerName----------Category
1 Int.Customer#1 3
2 Int.Customer#2 3
3 A-Mart.Branch#1 7
4 A-Mart.Branch#2 7
5 B-Mart.Branch#1 8
6 B-Mart.Branch#2 8
7 Grocery#1 9
8 Grocery#2 9
9 Grocery#3 9
10 Restaurant#1 10
11 Restaurant#2 10
12 Cafe#1 11
13 Wholeseller#1 6
14 Wholeseller#2 6
My requirement is something like this, "Given a node in Categories, Return All the Customers attached to any node below it".
How can I do it with sql?
Obviously this can be done with a recursive call in the code, but how can we do it in t-sql (without calling a stored procedure several times or using text-based search)?
Can any body, Use a CTE to solve this problem?
I have a result set of something like this in mind
CustomerID--------Customer Name----------------CategoryId----------CAtegoryName
12 Cafe#1 11 Cafes
12 Cafe#1 4 IndependentRetailers
12 Cafe#1 2 Demoestic
12 Cafe#1 1 AllCustomers
.
.
.
4 A-Mart.Branch#2 7 A-Mart
4 A-Mart.Branch#2 5 Chain Retailers
4 A-Mart.Branch#2 2 Domestic
4 A-Mart.Branch#2 1 All Customers
.
.
.
14 Wholeseller#2 6 WholeSellers
14 Wholeseller#2 2 Domestic
14 Wholeseller#2 1 All Customers
This is not necessarily a good Idea to layout a result like this, This would consume too much space, something that might not be required, yet, a search in such result set would be very fast. If I want to find all the customers below say categoryId = 2 , I would simply query
SELECT * FROM resultset where category ID = 2
Any suggestions to improve the data model is super welcomed! If It helps solving this problem.
Once again, I'm not fixated on this result set. Any other Suggestion that solves the problem,
"Given a node in Categories, Return All the Customers attached to any node below it", is well accepted.
You can use a CTE to recursively build a table containing all the parent-child relationships and use the where clause to get only the subtree you need (in my example, everyting under CategoryId 5) :
WITH CategorySubTree AS (
SELECT cc.CategoryId as SubTreeRoot,
cc.CategoryId
FROM CustomerCategory cc
UNION ALL
SELECT cst.SubTreeRoot, cc.CategoryId
FROM CustomerCategory cc
INNER JOIN CategorySubTree cst ON cst.CategoryId = cc.parentId
)
SELECT cst.CategoryId
FROM CategorySubTree cst
WHERE cst.SubTreeRoot = 5
You can modify this query to add whatever you need, for example, to get customers linked to the category nodes in the subtree :
WITH CategorySubTree AS (
SELECT cc.CategoryId as SubTreeRoot,
cc.CategoryId
FROM CustomerCategory cc
UNION ALL
SELECT cst.SubTreeRoot, cc.CategoryId
FROM CustomerCategory cc
INNER JOIN CategorySubTree cst ON cst.CategoryId = cc.parentId
)
SELECT cst.CategoryId,cic.CustomerId
FROM CategorySubTree cst
INNER JOIN CustomerInCategory cic ON cic.CategoryId = cst.CategoryId
WHERE cst.SubTreeRoot = 5
And of course you can join further tables to get labels and other needed information.
Supoose I have the following:
tbl_options
===========
id name
1 experience
2 languages
3 hourly_rate
tbl_option_attributes
=====================
id option_id name value
1 1 beginner 1
2 1 advanced 2
3 2 english 1
4 2 french 2
5 2 spanish 3
6 3 £10 p/h 10
7 3 £20 p/h 20
tbl_user_options
================
user_id option_id value
1 1 2
1 2 1
1 2 2
1 2 3
1 3 20
In the above example tbl_user_options stores option data for the user. We can store multiple entries for some options.
Now I wish to extend this, i.e. for "languages" I want the user to be able to specify their proficiency in a language (basic/intermediate/advanced). There will also be other fields that will have extended attributes.
So my question is, can these extended attributes be stored in the same table (tbl_user_options) or do I need to create more tables? Obviously if I put in a field "language_proficiency" it won't apply to the other fields. But this way I only have one user options table to manage. What do you think?
EDIT: This is what I propose
tbl_user_options
================
user_id option_id value lang_prof
1 1 2 null
1 2 1 2
1 2 2 3
1 2 3 3
1 3 20 null
My gut instinct would be to split the User/Language/Proficiency relationship out into its own tables. Even if you kept it in the same table with your other options, you'd need to write special code to handle the language case, so you might as well use a new table structure.
Unless your data model is in constant flux, I would rather have tbl_languages and tabl_user_languages tables to store those types of data:
tbl_languages
================
lang_id name
1 English
2 French
3 Spanish
tbl_user_languages
================
user_id lang_id proficiency hourly_rate
1 1 1 20
1 2 2 10
2 2 1 15
2 2 3 20
3 3 2 10
Designing a system that is "too generic" is a Turing tarpit trap for a relational SQL database. A document-based database is better suited to arbitrary key-value stores.
Excepting certain optimisations, your database model should match your domain model as closely as possible to minimise the object-relational impedance mismatch.
This design lets you display a sensible table of user language proficiencies and hourly rates with only two inner joins:
SELECT
ul.user_id,
u.name,
l.name,
ul.proficiency,
ul.hourly_rate
FROM tbl_user_languages ul
INNER JOIN tbl_languages l
ON l.lang_id = ul.lang_id
INNER JOIN tbl_users u
ON u.user_id = ul.user_id
ORDER BY
l.name, u.hour
Optionally you can split out a list of language proficiencies into a tbl_profiencies table, where 1 == Beginner, 2 == Advanced, 3 == Expert and join it onto tbl_user_languages.
i'm thinking it's a mistake to put "languages" as an option. while reading your text it seems to me that english is an option, and it might have an attribute from option_attributes.