mysql self join on parent_id but order alphabetically - mysql

I have the following tables. I've simplified the code for readability
CREATE TABLE entries (
ID int(),
entry varchar(),
parent_id int()
);
CREATE TABLE nouns (
entry_id int(),
plural varchar(),
[...]
);
CREATE TABLE verbs (
entry_id int(),
pres_part varchar(),
past_part varchar(),
[...]
);
CREATE TABLE definitions (
ID int(),
definition varchar(),
[...]
);
CREATE TABLE definitions_entries (
definition_id int(),
entry_id int()
);
and I am displaying all the entries on a glossary page as follows
SELECT *
FROM entries
LEFT JOIN nouns ON nouns.entry_id = entries.ID
LEFT JOIN verbs ON verbs.entry_id = entries.ID
LEFT JOIN definitions_entries ON entries.ID = definitions_entries.entry_id
LEFT JOIN definitions ON definitions_entries.definition_id = definitions.ID
ORDER BY entries.entry
But I want to reorder the list so that child-entries appear beneath their parents but that parent entries are still sorted alphabetically. I image the output would look something like this...
1 entry a null definition 1
4 entry a entry x definition 2
5 entry a entry y definition 3
2 entry b null definition 4
5 entry b entry z definition 5
3 entry c null definition 6
I've tried to follow this and this and this but I'm not getting anywhere.
I don't have any SQL examples left to show because they've all just gone to shit and I'm lost and confused.
The best result I got looked something like this though...
4 entry a entry x definition 2
5 entry a entry y definition 3
5 entry b entry z definition 5
1 entry a null definition 1 missing
2 entry b null definition 4 missing
3 entry c null definition 6 missing
I'm running MySQL 5.6 so I can't use RECURSIVE WITH. Not that I even understand if that would help me.

Related

Get related table's related record in mysql

I have three tables ClaimHeader, ResClaim and ResActivity. ClaimHeader table's primary key is used as foreign key in ResClaim table and ResClaim table's primary key is used as foreign key in ResActivity table.
Below is my tables
ClaimHeader:
HeaderID FileID FileName
1 fileid1 file1.xml
2 fileid2 file2.xml
3 fileid3 file3.xml
4 fileid4 file4.xml
--------------------------------------------------
ResClaim:
ClaimPKID HeaderPKID ClaimDateSettlement
1 1 2017-04-08
2 1 2017-03-08
3 2 2017-04-10
4 3 2017-05-08
--------------------------------------------------
ResActivity:
ActivityPKID ClaimPKID ActivityNet
1 1 400
2 2 3000
3 2 2030
4 3 5000
Tables screenshot
ResClaim table uses HeaderPKID as the foreign key from ClaimHeader table and ResActivity table uses ClaimPKID as the foreign key from ResClaim table
My scenario is i should display related record from all the three tables.
For example i want to display FileID from ClaimHeader table , Total claims count from ResClaim table and Sum of ActivityNet from ResActivity table with the matching condtion.
My expected result would be:
FileID | Total Claim(s) | ActivityNet
--------------------------------------------------
fileid2 | 1 | 5030 (3000+2030)
--------------------------------------------------
I have tried below query:
SELECT
`ClaimHeader`.*,
count(ResClaim.ClaimPKID) as claims,
sum(ResActivity.ActivityNet) as net
FROM
`ClaimHeader`
RIGHT OUTER JOIN `ResClaim`
ON ResClaim.ClaimPKID = ClaimHeader.HeaderID
RIGHT OUTER JOIN `ResActivity`
ON ResClaim.ClaimPKID = ResActivity.ActivityPKID
The above query is not returning related record values - instead of that it's returning sum of all the columns and count from ResActivity and ResClaim table.
It is still not completely clear what you are asking. It seems you either want one result row per claim header or one result row per file. So group by the column in question.
By joining all records you get of course claim headers and claims multifold, e.g. with
claim headers: head1, head2
claims for head1: claim10, claim11
claims for head2: claim20, claim21
actions for claim 10: action100, action101
actions for claim 11: action110, action111
actions for claim 20: action200, action201
you get this intermediate result from the joins:
header | claim | action
-------+---------+----------
head1 | claim10 | action100
head1 | claim10 | action101
head1 | claim11 | action110
head1 | claim11 | action111
head2 | claim20 | action200
...
Now by grouping per header, you get one aggregated result row for head1, one for head2. As the intermediate result contains one record per action, you can easily sum them. But as to claims: there are four records for head1, and if you do count(*) or count(claimpkid) you get a result of 4 (count(claimpkid) counts all claimpkid that are not null, which is always the case in this example). What you want to do instead is counting distinct claims (namly two here: claim10 and claim11). So use COUNT DISTINCT.
select
h.headerid,
count(distinct c.claimpkid) as claims,
coalesce(sum(a.activitynet), 0) as net
from claimheader h
left outer join resclaim c on c.claimpkid = h.headerid
left outer join resactivity a on a.activitypkid = c.claimpkid
group by h.headerid
order by h.headerid;
I am using left outer joins (and COALESCE) for the case that a header has no claims or a claim has no actions, for which we would show zeros rather then removing them from the results.
As mentioned, if you want this per file, then select fileid and group by it instead of headerid.

Mysql select IN another table row for each returned result

I have two tables section and users. I need to run a simple query to return all sections. However for each section there MAY be a corresponding id in a field with multiple ids in the users section. ie ids = 2,6,8,10
Here is an example for selecting a specific section by id and its assigned users.
Select * from section, users where sectionid = '2' and sectionid IN (`ids`);
This would return all the user.ids where 2 is within ids
My problem is I need to select all of the users assigned to each section in one table?
section
------------------------------------
sectionid, sectionname, Description
2 section2
4 section4
6 section6
8 section8
User
------------------------------------
userid, ids(of the section/s),
1 4,6
2 4,8
3
4 4,6,8
Desired result: Display ALL sections whether or not users are assigned to and in one column display the userid/s assignes to each section as below.
Result
-------------------
Sectionid, sectionname, usersassignedtothissection
2 section2 null (no one assigned to section2)
4 section4 1,2,4
6 section6 1
8 section8 2,4
You can use the following solution, using FIND_IN_SET to JOIN the tables:
SELECT
section.sectionid,
section.sectionname,
GROUP_CONCAT(user.userid ORDER BY user.userid) AS 'usersassignedtothissection'
FROM section
LEFT JOIN user ON FIND_IN_SET(section.sectionid, user.ids)
GROUP BY section.sectionid, section.sectionname
demo: http://sqlfiddle.com/#!9/f9b860/5/0
Note: It is not recommended to use a column to store multiple values! You can create a mapping table to map the table section to table user like the following:
CREATE TABLE section_user (
user_id INT,
section_id INT
)
In this case your query would be the following:
SELECT
s.sectionid,
s.sectionname,
GROUP_CONCAT(u.userid ORDER BY u.userid) AS 'usersassignedtothissection'
FROM section s
LEFT JOIN section_user su ON s.sectionid = su.section_id
LEFT JOIN user u ON su.user_id = u.userid
GROUP BY s.sectionid, s.sectionname
demo: http://sqlfiddle.com/#!9/432059/2/0

Table with multiple items in one field

I'm pretty much a MySql newbie so sorry if this sounds daft or I ramble off topic.
I am building a table to list all the data about exhibitions. This comprises fields with data like venue, dates, times, etc and one field with a list of items exhibited. This field will have a delimited list of numbers. They will between 1 and 50 numbers in the range 1 to 999, currently the num_max is 170.
I realise that it would be better practice to hold this data in separate tables but that complicates the uploading process, would require a new table being created for each new exhibition and give rise to more opportunity for errors.
Assuming that this strategy is correct, my real problem is in processing the data.
How do I extract the list of numbers then use it to get an array of product numbers from the master product table?
You should only need one more table that holds items to be included in an exhibit. This table could have several rows for one exhibit, like so:
exhibit_id item_id description
______________________________________________
1 1 painting
1 2 statue
Then, all you would need to do is join the two tables together.
Otherwise, if you did not want to do this, use the php explode method to turn a delimited string into an array.
$data = mysql_query($query);
while ($row = mysql_fetch_assoc($result)) {
$items = $row['items'];
//the explode method in php allows you to turn a delimited string into an array.
$items_array = explode(',', $items);
//loop through each of the items in this exhibit
foreach($items_array as $current_item) {
//do something with $current_item
}
}
If you are going to hold a field that has data such as "2,45,67,126" then you're going to have to process that using the language in which you're extracting it, perhaps PHP.
The 'real' solution would be to have a unique identifier on the table holding the exhibitions and have a second table with the items. So for example, you'd have an id of '42' for the exhibition then a second table (called, perhaps 'items') holding:
id item
42 2
42 45
42 67
42 126
That shouldn't complicate your upload process too much and then you can easily return all the items in an exhibition using:
SELECT item FROM items WHERE id=$exhibition_id
Actually, it wouldn't require a new table for each exhibit, just adding rows to an existing table.
The rows in the child table would be "related" to the parent table by a foreign key.
For example
exhibit
( id int auto_increment primary key comment 'PK unique identifier for exhibit'
, exhibit_venue
, exhibit_date
et al.
)
exhibit_item
( exhibit_id int comment 'fk to exhibit.id'
, item_id int comment 'fk to item.id'
, primary_key (exhibit_id, item_id)
, foreign_key exhibit_items_fk1 (exhibit_id) references exhibit (id)
, foreign_key exhibit_items_fk2 (item_id) references item (id)
)
item
( id int auto_increment primary key comment 'PK unique identifier for item'
, name
, description
, size
, weight
et al.
)
exhibit
id venue date
---- ------- -----------
123 Fox 2014-02-24
124 Ice 2014-03-01
item
id name description
---- -------- -----------
41 madonna painting
42 david sculpture
43 mona lisa painting
exhibit_item
exhibit_id item_id
---------- -------
123 41
123 42
123 43
If you need to store a relative sequence or position (relative order) of the items within an exhibit, you can add another attribute to the exhibit_item table, to store an integer representing the position.
To get all items for one exhibit:
SELECT i.id
, i.name
, i.description
FROM exhibit_item s
JOIN item i
ON i.id = s.item_id
WHERE s.exhibit_id = 123
ORDER BY s.position
If it's handier for you to return a comma separated list of the item ids within an exhibit, as a single string...
SELECT GROUP_CONCAT(s.id ORDER BY s.position) AS item_list
FROM exhibit_item s
WHERE s.exhibit_id = 123

Tricky multiple where condition MySQL Query

I have a table customer, a table with different criterias (These criterias are then used to make ratings). Another table has the values and its keys in it.
table company
==============
int company_id
varchar name
bool status
table criteria
==============
int criteria_id
varchar name
bool status
table company_criteria
==============
int company_id
int criteria_id
int criteria_value
varchar comments
Now i display all the criterias in the form of select boxes which will have themselves a value against each criteria (already in the DB). Now i want the user to be able to search for different companies who have those specific criteria and the stored value.
e.g: table customer has a record with id 1
table criteria has records
1--->Reputation, 2--> Salary
table company_criteria has following records:
company_id | criteria_id | criteria_value |
============================================
1 1 10
1 2 20
Now the user sees two select boxes (remember there are two records in the criteria table) - with different options. He selects 10 from first select box and 20 from the second select box. How would i write the Query - I tried the following
SELECT DISTINCT `co`.*
FROM (`company` co)
JOIN `company_criteria` cc ON `co`.`company_id` = `cc`.`company_id`
WHERE (`cc`.`criteria_id`=1 AND `cc`.`criteria_value`>=10) AND (`cc`.`criteria_id`=2 AND `cc`.`criteria_value`>=20)
It just doesn't work - gives me no results - always. Appreciate any help - thanks.
Probably you want to put and OR instead of the AND here
SELECT DISTINCT `co`.*
FROM (`company` co)
JOIN `company_criteria` cc ON `co`.`company_id` = `cc`.`company_id`
WHERE (`cc`.`criteria_id`=1 AND `cc`.`criteria_value`>=10) OR (`cc`.`criteria_id`=2 AND `cc`.`criteria_value`>=20)
between the 2 where conditions....they both cant be true at the same time I think

Finding values from a table that are *not* in a grouping of another table and what group that value is missing from?

I hope I am not missing something very simple here. I have done a Google search(es) and searched through Stack Overflow.
Here is the situation: For simplicity's sake let's say I have a table called "PeoplesDocs", in a SQL Server 2008 DB, that holds a bunch of people and all the documents that they own. So one person can have several documents. I also have a table called "RequiredDocs" that simply holds all the documents that a person should have. Here is sort of what it looks like:
PeoplesDocs:
PersonID DocID
-------- -----
1 A
1 B
1 C
1 D
2 C
2 D
3 A
3 B
3 C
RequiredDocs:
DocID DocName
----- ---------
A DocumentA
B DocumentB
C DocumentC
D DocumentD
How do I write a SQL query that returns some variation of:
PersonID MissingDocs
-------- -----------
2 DocumentA
2 DocumentB
3 DocumentD
I have tried, and most of my searching has pointed to, something like:
SELECT DocID
FROM DocsRequired
WHERE NOT EXIST IN (
SELECT DocID FROM PeoplesDocs)
but obviously this will not return anything in this example because everyone has at least one of the documents.
Also, if a person does not have any documents then there will be one record in the PeoplesDocs table with the DocID set to NULL.
How about something like this:
Select ...
From RequiredDocs As RD
Cross Join People As P
Where Not Exists(
Select 1
From PeoplesDocs As PD1
Where PD1.PersonId = P.PersonId
And PD1.DocId = RD.DocId
)
SELECT
p.PersonID,
rd.DocName AS MissingDocs
FROM
dbo.People p, dbo.RequiredDocs rd
WHERE
rd.DocID NOT IN (SELECT pd.DocID FROM dbo.PeoplesDocs pd
WHERE pd.PersonID = p.PersonID)