Select distinct values based column keys placed in different table - mysql

I am new to MySQL I am facing a wall. I was looking for solution but could not find anything which would match my case. I understand how joins working. My question is: Is it possible to do a SELECT based on result from different SELECT?
For clarifying I have 2 tables and 1 view:
report_type table:
id | name | view_name
-------------------------------
1 | citizens | citizens_report
report_filter table:
id | name | filter_column
-------------------------------
1 | City | city
2 | Nationality | nationality
citizens_report view:
id | city | nationality
-------------------------------
1 | Boston | American
2 | London | British
3 | London | Spanish
4 | Paris | French
5 | Paris | French
6 | Boston | German
7 | New York | American
For raportId = 1 I need to look for dynamic view based on result from: report_type table which will be citizens_report - so here is first step where I need to build query with result of it and than I need to create unions of filters based on citizens_report view.
Expected result:
filter | option
----------------------------
city | Boston
city | London
city | Paris
city | New York
nationality | French
nationality | German
nationality | American
nationality | British
nationality | Spanish
I doesn't have to be in any specific order (might be ORDER BY ASC). In any language I can create map where key is equal filter and option is added to array.
Each step I can do with separate query and in any programming language I can call for next one, but could it be done in one query?
Thanks!

Related

SQL query that searches comma-delimited field

I have a student table which looks something like this:
id | name | school_descriptors
-------------------------------------------------------
1 | Rob | Comp Sci,Undergraduate,2020
2 | Tim | Business,MBA,2022
3 | Matt | Business,MBA,2022
4 | Jack | Law,Masters,2024
5 | Steph | Comp Sci,Masters,2022
The school_descriptors field is just one column, and stores information about the Course, Qualification and Graduation year as a comma-delimited string. (it's terribly designed and I wish it could be split up into its own fields, but it can't right now (I am not the database owner))
I want to provide an interface where teachers can quickly find students that match certain Course, Qualifications and Graduation years, and thus would like to create relevant queries.
Question 1: For example, I would like a teacher to be able to select from the UI: "Business", "MBA" and get returned students with ID 2 and 3. Specifically, an example question I have is: Find students who are in the Business Course and doing the MBA qualification:
SELECT * FROM student_table WHERE school_descriptors LIKE '%Business%' AND school_descriptors LIKE '%MBA%'
The query I have in mind is a basic LIKE query, but I can't help but think there is a more efficient query that can take advantage of the fact that the school_descriptor string is 1) always in a specific order (e.g. course, qualification, graduation), and 2) comma-delimited, and thus could be perhaps split. The table currently sits at ~5000 rows so relatively small but is expected to grow.
Related question 2: Find students who are in the Comp Sci Course and graduating after 2019:
Would it be possible to split the school_descriptors field and add a >2019 operand?
Many thanks!
In MySql you can use the function SUBSTRING_INDEX() to split the column school_descriptors.
This will work only if the positions of Course, Qualification and Graduation year are fixed.
select *,
substring_index(school_descriptors, ',', 1) Course,
substring_index(substring_index(school_descriptors, ',', 2), ',', -1) Qualification,
substring_index(school_descriptors, ',', -1) Graduation
from student_table
See the demo.
Results:
> id | name | school_descriptors | Course | Qualification | Graduation
> -: | :---- | :-------------------------- | :------- | :------------ | :---------
> 1 | Rob | Comp Sci,Undergraduate,2020 | Comp Sci | Undergraduate | 2020
> 2 | Tim | Business,MBA,2022 | Business | MBA | 2022
> 3 | Matt | Business,MBA,2022 | Business | MBA | 2022
> 4 | Jack | Law,Masters,2024 | Law | Masters | 2024
> 5 | Steph | Comp Sci,Masters,2022 | Comp Sci | Masters | 2022
select id, name,
substring_index(school_descriptors,',',1) as course,
substring_index(substring(school_descriptors,length(substring_index(school_descriptors,',',1))+2,200),',',1) as Qualifications,
substring_index(school_descriptors,',',-1) as year
from student;
output:
+------+-------+----------+----------------+------+
| id | name | course | Qualifications | year |
+------+-------+----------+----------------+------+
| 1 | Rob | Comp Sci | Undergraduate | 2020 |
| 2 | Tim | Business | MBA | 2022 |
| 3 | Matt | Business | MBA | 2022 |
| 4 | Jack | Law | Masters | 2024 |
| 5 | Steph | Comp Sci | Masters | 2022 |
+------+-------+----------+----------------+------+
A link to the docs, in case you want to know about SUBSTRING_INDEX()
Answer 1:
SELECT * FROM student_table WHERE school_descriptors REGEXP ['Business','MBA']
By using this query you can get all the records that are having Business OR MBA.
If you want to select only Business, MBA you can try like this
SELECT * FROM student_table WHERE school_descriptors LIKE '%Business,MBA%'
Answer 2:
SELECT *
FROM student
WHERE
SUBSTRING_INDEX(SUBSTRING_INDEX(school_descriptors , ',', 1), ',', -1)='Comp Sci'
AND
SUBSTRING_INDEX(SUBSTRING_INDEX(school_descriptors , ',', 3), ',', -1)> 2019;

How to order the rows in Laravel based on a column's value?

I have a table of products with city, state and country name.
If a user visits my shop, the products from his city should be displayed first, then the products from his state and finally the products from his country.
For a table,
+----+---------+-------------+------------+---------------+|
| id | product | city | state | country |
+----+---------+-------------+------------+---------------+
| 1 | guava | Julian | California | United States |
| 2 | apple | London | NA | England |
| 3 | orange | Los Angeles | California | United States |
| 4 | grapes | Zion | Illinois | United States |
| 5 | banana | Canyon | California | United States |
+----+---------+-------------+------------+---------------+
if a user from Los Angeles visits my shop, the expected results should be of order 3,1,5,3,2.
I read the laravel docs and also tried searching in stack overflow but couldn't find any apt answers.
My code for now is
Products::where('city",$usercity)->orWhere('state',$userstate)
->orWhere('country',$usercountry)->get();
I don't have an idea on what to use for the orderBy part!
You can use
Case
which returns a value on a specified condition, In your case (no pun intended) it returns the order of products.
We will use orderByRaw() to integrate the case statement.
But
you need to be careful because this will cause SQL injection And we don't want that so we use bindings:
Products::where('city',$usercity)
->orWhere('state',$userstate)
->orWhere('country',$usercountry)
->orderByRaw('
CASE
WHEN city = ? THEN 1
WHEN state = ? THEN 2
WHEN country = ? THEN 3
ELSE 4
END',[$usercity,$userstate,$usercountry])->get();
And that will got you the desired result hopefully.
P.S: Follow Laravel naming conventions which says: Model's names should be singular not plural (Product not Products).

Single MySQL query to return different categorized averages

I have a MySQL database which contains a table with 3 columns: survey, type and response. Each survey can have multiple types and each type can have multiple responses. Response is an integer.
|--------|-------|----------|
| survey | type | response |
|--------|-------|----------|
| Food | Men | 4 |
|--------|-------|----------|
| Food | Men | 5 |
|--------|-------|----------|
| Food | Women | 1 |
|--------|-------|----------|
| Food | Women | 3 |
|--------|-------|----------|
| Drink | Old | 3 |
|--------|-------|----------|
| Drink | Old | 5 |
|--------|-------|----------|
| Drink | Young | 1 |
|--------|-------|----------|
Is it possible, in a single SQL query, to return the average response for all responses from the same surveys AND for all responses from the same type?
Using the following SQL with GROUP BY returns averages for each type
`SELECT survey, type, AVG(response) FROM 'test' GROUP BY survey, type`
but is there a way to modify the query to also return survey averages (which are a combination of types) to output something like below?
|--------|-------|---------------|
| survey | type | AVG(response) |
|--------|-------|---------------|
| Food | Men | 4.5 |
|--------|-------|---------------|
| Food | Women | 2 |
|--------|-------|---------------|
| Food | ----- | 3.25 |
|--------|-------|---------------|
| Drink | Old | 4 |
|--------|-------|---------------|
| Drink | Young | 1 |
|--------|-------|---------------|
| Drink | ----- | 2.5 |
|--------|-------|---------------|
Currently I have to use a loop to input new survey and type values to retrieve both of these average responses however if this can be achieved without a loop, in a single query, it would be more efficient.
I'm using a very simplified table in this example to illustrate the question :)
You can also give a try using WITH ROLLUP option
create table t(survey varchar(50),type varchar(50), response int);
insert into t values('Food','Men',4);
insert into t values('Food','Men',5);
insert into t values('Food','Women',1);
insert into t values('Food','Women',3);
insert into t values('Drink','Old',3);
insert into t values('Drink','Old',5);
insert into t values('Drink','Young',1);
select survey,type,avg(response)
from t
group by survey,type with rollup;
You can make use of OVER clause with PARTITION BY for this
SELECT survey,
type,
AVG(response) AS AVGTYPE,
AVG(response) OVER(PARTITION BY survey) AS AVGSURVEY
FROM test
GROUP BY survey, type

MySQL query or stored procedure that calls itself recursively and returns all nodes for selected parent

id | parent_id | name
-------------------------
1 | null | World
2 | 1 | Sri Lanka
3 | 1 | America
4 | 2 | South Province
5 | 2 | Western Province
6 | 4 | Galle
7 | 6 | Wakwella
8 | 3 | New York
I need a MySQL query or stored procedure that calls itself recursively and returns all nodes,child nodes and leaf nodes for selected "id" .
As a example:
When i want to select all child of id=2
Result should be,
South Province
Western Province
Galle
Wakwella
When i want to select all child of id=3
Result should be,
New York
a similar question was answered here:
https://dba.stackexchange.com/questions/7147/find-highest-level-of-a-hierarchical-field-with-vs-without-ctes/7161#7161
You need to use stored procedures for this.

How to structure this sql query to create the following new table?

Assume I have the following tables:
tableA
a_name | age | country
Jordan | 5 | Germany
Molly | 6 | Spain
Paris | 7 | France
tableB
b_name | age | country
Kyle | 5 | Germany
Bob | 6 | Spain
Bob | 7 | Spain
Stephen | 7 | France
Kyle | 9 | France
Mario | 2 | Mexico
I want to make it such that I can produce a tableC that contains:
id (auto increment primary key) | age | country | country_marker
1 | 5 | Germany | 1
2 | 6 | Spain | 2
3 | 7 | France | 3
4 | 7 | Spain | 2
5 | 8 | France | 3
6 | 9 | France | 3
7 | 2 | Mexico | 4
For the new table:
takes any unique "age, country" pair only and putting them into tableC with "country_marker" automatically assigning incrementing unique numbers based on distinct "country"
Note there is no countries table, as the "country" in tableC is just based on whatever countries are in tableA, and tableB and country_marker is just a system generated identifier to indicate the unique countries in the table. Output tableC "id and country marker" ordering does not matter as long as it meets the bullet above.
I have painfully tried to produce this using MySQL cursors and want to know if its possible/what is some SQL query or set of queries I could use that would make this faster than using cursors.
I'd approach this by first creating table C in something like phpmyadmin. Then I'd write a query to pull the information from tables A and B that I want to put into C. Then, within phpmyadmin, I'd export the results of the query to a .sql file on my local machine. I'd open the .sql file to make the necessary modifications to the INSERT statements so that I can import the data in the .sql file to table C.
This isn't exactly the most efficient way, but it seems like the least technical way that would definitely work.