I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but canĀ“t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.
Related
I am writting a simple application that is ordering my medias (pictures, music, videos...). Each media can ben associated with 0 to many tags.
My goal is to have a UI where I can search my medias (for exemple, show images and videos tagged like %hol%, and return both holidays tagged photos and hollywood tagged photos).
Here's my database :
Table medias
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| path | varchar(400) | NO | UNI | NULL | |
| type | varchar(5) | NO | | NULL | |
| libelle | varchar(200) | NO | | NULL | |
| ratings | int(2) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
Table tags
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| libelle | varchar(200) | NO | UNI | NULL | |
+---------+--------------+------+-----+---------+----------------+
Table medias_tags
+----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| id_media | int(11) | NO | PRI | NULL | |
| id_tag | int(11) | NO | PRI | NULL | |
+----------+---------+------+-----+---------+-------+
As I have many medias, I had to limit the result. So in my front-end, I made a pagination system, and query my medias according to the page I am (for exemple, if I am on page 3, I put LIMIT 20 OFFSET 60 in my sql statement).
Now I'm trying to filter my medias. I have a searchbar, and if I type 'hol', I want to get 20 medias with tagged like '%hol%' (holidays, hollywood...)
Getting filtered medias works, but I don't know how to get exactly 20 medias.
Here's my sql query without filtering:
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM (select * from medias LIMIT ? OFFSET ?) medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
And here's my filtering sql query:
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
WHERE tags.libelle LIKE ? [OR tags.libelle LIKE ? ...]
(last parameters are my tags)
Both query work well, but I can't find a way to limit my filtered result. Here's a sample of my filtering query result :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wallpapper |
+----+-------------+-------+-------------------+---------+------------+
How can I limit my filtering result to only return n different medias id ? Is there a pure sql solution ? Maybe with stored procedures ?
Thanks !
EDIT :
Here's a result I'd like with limit = 7 :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 7 | mock/01.jpg | PHOTO | NEWLY ADDED MEDIA | 8 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 4 | mock/03.jpg | PHOTO | Purple 2 | 5 | wallpapper |
| 5 | mock/03.jpg | PHOTO | Purple 3 EDITED | 8 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wallpapper |
+----+-------------+-------+-------------------+---------+------------+
I have 9 rows, but only 7 distincts media id. Every media has a tag like '%a%'.
EDIT 2 : someone posted an answer, but deleted it. His idea was to concatenate tags, which would be a nice solution too.
Something like that :
+----+-------------+-------+-------------------+---------+------------+
| id | path | type | libelle | ratings | tag |
+----+-------------+-------+-------------------+---------+------------+
| 11 | mock/02.jpg | PHOTO | 02.jpg | 0 | dark |
| 7 | mock/01.jpg | PHOTO | NEWLY ADDED MEDIA | 8 | wallpapper |
| 2 | mock/02.jpg | PHOTO | Night | 5 | wallpapper, dark |
| 1 | mock/03.jpg | PHOTO | Purple | 5 | wallpapper |
| 4 | mock/03.jpg | PHOTO | Purple 2 | 5 | wallpapper |
| 5 | mock/03.jpg | PHOTO | Purple 3 EDITED | 8 | wallpapper |
| 3 | mock/01.jpg | PHOTO | Wave | 5 | wave, wallpapper |
+----+-------------+-------+-------------------+---------+------------+
But I have no idea how to write this sql query...
Use GROUP_CONCAT in order to build a tag string per media and outer join this result. Then apply your limit clause as desired
select
medias.id,
medias.path,
medias.type,
medias.libelle,
medias.ratings,
mtags.tags
from medias
left outer join
(
select id_media, group_concat(tags.libelle order by tags.libelle) as tags
from medias_tags
join tags on tags.id = medias_tags.id_tag
group by id_media
) mtags on mtags.id_media = medias.id
order by medias.id
limit 20 offset 60;
Are you expecting like this?
SELECT
medias.id, medias.path, medias.type, medias.libelle as libelle, medias.ratings, tags.libelle as tag
FROM medias
left outer join medias_tags on medias.id = medias_tags.id_media
left outer join tags on tags.id = medias_tags.id_tag
WHERE tags.libelle LIKE ? [OR tags.libelle LIKE ? ...]
order by medias.id
limit 0,10
Here limit is used for first 10 record. you can use stored procedure for passing the two params of limit and pick the filtered result
I'm trying to break up a SQL table that needs to take a users name and find the unique user ID's from up to 4 systems.
The data is currently like this:
| Name | User_ID |
-----------------
| A | 10 |
| A | 110 |
| A | 1500 |
| A | 4 |
| B | 20 |
| B | 100 |
| B | 2 |
| C | 10 |
I need to pivot it around the user's name to look like this (the id's don't need to be in numerical order as the SYS#_ID for each doesn't matter):
| Name | SYS1_ID | SYS2_ID | SYS3_ID | SYS4_ID |
------------------------------------------------
| A | 4 | 10 | 110 | 1500 |
| B | 2 | 20 | 100 | NULL |
| C | 10 | NULL | NULL | NULL |
This is the code I have tried on MySQL:
PIVOT(
COUNT(User_ID)
FOR Name
IN (SYS1_ID, SYS2_ID, SYS3_ID, SYS4_ID)
)
AS PivotedUsers
ORDER BY PivotedUsers.User_Name;
I'm unsure if PIVOT works on MySQL as I keep getting an error "PIVOT unknown". Is there a way to find the values that each user has and if they do not appear in the table already add them to the next column with a max of 4 values?
Prior Information Notice
I have 3 tables:
types
+----+-------------+-----------------------+------------+------------+
| id | category_id | name | created_at | updated_at |
+----+-------------+-----------------------+------------+------------+
| 1 | 1 | T-Shirts | NULL | NULL |
+----+-------------+-----------------------+------------+------------+
prototypes
+----+-----------------------------------------+------------+------------+
| id | name | created_at | updated_at |
+----+-----------------------------------------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL |
+----+-----------------------------------------+------------+------------+
filters
+----+-------------+---------------------+-------+------------+------------+
| id | name | value | extra | created_at | updated_at |
+----+-------------+---------------------+-------+------------+------------+
| 1 | gender | male | NULL | NULL | NULL |
| 2 | gender | female | NULL | NULL | NULL |
| 3 | age_group | adult | NULL | NULL | NULL |
| 4 | age_group | child | NULL | NULL | NULL |
| 5 | age_group | baby | NULL | NULL | NULL |
+----+-------------+---------------------+-------+------------+------------+
They are related one another through n-m relationship, so there are respective junction tables types_prototypes, types_filters, prototypes_filters as well. For more details please check also out my dump file.
Problem itself
I'm trying to set up filtering system (with Laravel), so I need to query all Prototypes that are related to all given Filters (logical AND). Until now I have managed to get the, as long as use chose only one Filter:
select * from `prototypes`
inner join `types_prototypes` on `prototypes`.`id` = `types_prototypes`.`prototype_id`
inner join `prototypes_filters` on `prototypes`.`id` = `prototypes_filters`.`prototype_id`
inner join `filters` on `prototypes_filters`.`filter_id` = `filters`.`id`
where `types_prototypes`.`type_id` = ? and `filter_id` = ? group by `prototypes`.`id`
The problem itself consists in the fact that this query is inapplicable, as soon as we have several filters that should be valid simultaneously:
...
where `types_prototypes`.`type_id` = ? and `filter_id` = ? and `filter_id` = ? group by `prototypes`.`id`
I know, where ... and doesn't work, because I have due to join only one column filter_id that can contain only one single value at the same time (what actually groupBy() takes care of). So in this sense I have a new one row for the relation of the same Prototype with another Filter, e.g.:
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| id | name | created_at | updated_at | type_id | prototype_id | prototype_id | filter_id | id | name | value | extra | created_at | updated_at |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 3 | 3 | age_group | adult | NULL | NULL | NULL |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
I have already tried several different methods, including where 'filter_id' in(?,?), where FIND_IN_SET('filter_id', '?,?') and even restructured my database in accord with EAV-pattern (when the filters is divided into filter_names and filter_values). But every time I obtain only entries that fulfill one requirement of the whole set (equals logical OR), for instance (here we have prototypes for adults and men, but not only for adult men):
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| id | name | created_at | updated_at | type_id | prototype_id | prototype_id | filter_id | id | name | value | extra | created_at | updated_at |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 2 | American Apparel Womans T-Shirt | NULL | NULL | 1 | 2 | 2 | 3 | 3 | age_group | adult | NULL | NULL | NULL |
| 3 | Gildan Adult Cotton T-shirt | NULL | NULL | 1 | 3 | 3 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 4 | American Apparel Mens T-Shirt | NULL | NULL | 1 | 4 | 4 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 5 | American Apparel Kids T-Shirt | NULL | NULL | 1 | 5 | 5 | 1 | 1 | gender | male | NULL | NULL | NULL |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
I'm almost desperate, does anybody have a clue?
Thanks you in advice for your help and sorry for so much text, I just wanted to describe all circumstances.
You have to join with the filters table repeatedly for each criterion.
select * from prototypes AS p
inner join types_prototypes AS tp1 on p.id = tp1.prototype_id
inner join prototypes_filters AS pf1 on p.id = pf1.prototype_id
inner join filters AS f1 on pf1.filter_id = f1.id
inner join types_prototypes AS tp2 on p.id = tp2.prototype_id
inner join prototypes_filters AS pf2 on p.id = pf2.prototype_id
inner join filters AS f2 on pf2.filter_id = f2.id
where tp1.type_id = ? and f1.filter_id = ?
AND tp2.type_id = ? and f2.filter_id = ?
group by prototypes.id
For this question I have created a simple example that illustrates what I am asking.
Say I had a table called 'books'
+----+----------------------------+-----------+
| pk | title | author_id |
+----+----------------------------+-----------+
| 1 | The Lost Symbol | 1 |
| 2 | Follow Us Home | 2 |
| 3 | The Man in the High Castle | 3 |
+----+----------------------------+-----------+
(table a)
And another table called 'shops', that had a list of shops that sold each book:
+----+---------+-------------+-------+
| pk | book_id | shop_name | price |
+----+---------+-------------+-------+
| 1 | 1 | WHSmith | 5.00 |
| 2 | 1 | Waterstones | 7.00 |
| 3 | 1 | Amazon | 2.50 |
| 4 | 2 | WHSmith | 4.00 |
| 5 | 2 | Borders | 4.50 |
+----+---------+-------------+-------+
(table b)
If I do a simple select that grabs a book and all of the places it is sold using a join such as:
SELECT
books.*,
shops.shop_name,
shops.price
FROM
books
JOIN shops ON books.pk = shops.book_id
WHERE
book.book_name = "The Lost Symbol"
I would get results such as below:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table c)
However, I would LIKE to receive results like this:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table d)
I.e. the first row is just the result of left outer join and the rest of the results are the the inner join.
An even more desired outcome is:
+------+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+------+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| NULL | NULL | NULL | WHSmith | 5.00 |
| NULL | NULL | NULL | Waterstones | 7.00 |
| NULL | NULL | NULL | Amazon | 2.50 |
+------+-----------------+-----------+-------------+-------+
(table e)
Having shop_name and price concatenated and grouped in a single row seems not to work as it only does the first result from shops instead of all of them, also in my real world scenario, I have punctuation in the data so have to be careful with the separator.
So how would I get the result of table e?
You can use UNION ALL to build the required result set:
SELECT pk, title, author_id, NULL AS shop_name, NULL AS price
FROM books
WHERE books.title = "The Lost Symbol"
UNION ALL
SELECT NULL AS pk, NULL AS title, NULL AS author_id, shops.shop_name, shops.price
FROM books
JOIN shops ON books.pk = shops.book_id
WHERE books.title = "The Lost Symbol"
The first part of the union operation returns the first row of the result, i.e. the book title. The second part returns the rest of the rows, i.e.the shop names.
Demo here
I have a query:
select
count(*), paymentOptionId
from
payments
where
id in (select min(reportDate), id
from payments
where userId in (select distinct userId
from payments
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56))
group by userId)
group by
paymentOptionId;
The problem place is "select min(reportDate), id", this query must return 1 column result, but I can't realize how to do it while I need to group min.
The data set looks like
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| id | userId | amount | userLevel | reportDate | buffId | bankQuot | paymentOptionId |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| 9 | 12012 | 5 | 5 | 2014-02-10 23:07:57 | NULL | NULL | 2 |
| 10 | 12191 | 5 | 6 | 2014-02-10 23:52:12 | NULL | NULL | 2 |
| 11 | 12295 | 5 | 6 | 2014-02-11 00:12:04 | NULL | NULL | 2 |
| 12 | 12295 | 5 | 6 | 2014-02-11 00:12:42 | NULL | NULL | 2 |
| 13 | 12256 | 5 | 6 | 2014-02-11 00:26:25 | NULL | NULL | 2 |
| 14 | 12256 | 5 | 6 | 2014-02-11 00:26:35 | NULL | NULL | 2 |
| 16 | 12510 | 5 | 5 | 2014-02-11 00:42:58 | NULL | NULL | 2 |
| 17 | 12510 | 5 | 5 | 2014-02-11 00:43:08 | NULL | NULL | 2 |
| 18 | 12510 | 18 | 5 | 2014-02-11 00:45:16 | NULL | NULL | 3 |
| 19 | 12510 | 5 | 6 | 2014-02-11 01:00:10 | NULL | NULL | 2 |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
select count(*), paymentOptionId
from
(select userId, min(reportdate), paymentOptionId
from payments as t1
group by userId, paymentOptionId) as t2
group by paymentOptionId
Fiddle
It first gets the minimum report date (so the first entry) for every user, for every type (so there are two records for a user who has 2 types) and then counts them grouping by type (aka paymentOptionId).
By the way, you can of course cut the attributes chosen in select in from clause, they are only there so you can copy-paste it and see the results it is giving step by step.
You seem to want to report on various payment options and their counts for the earliest ReportDate for each user.
If so, here is an alternative approach
select p.paymentOptionId, count(*)
from payments p
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56) and
not exists (select 1
from payments p2
where p2.userId = p.userId and
p2.ReportDate < p.ReportDate
)
group by paymentOptionId;
This isn't exactly the same as your query, because this will only report on the list of payment types, whereas you might want the first payment type for anyone who has ever had one of these types. I'm not sure which you want, though.