PHP and MySQL
This is an existing project with a poor design and table layout.. that has been requested to have a new 'feature' added.
I'm wondering if a query like this can be put together (not the greatest with query stuff)
Quick Summary:
A form post is made.
table it gets a messageid and a (blank) subid
if a NEW post is made.. the above happens again..
(messageid and a (blank) subid)
if a 'comment' to an existing post is made..
table it gets a messageid and a subid that matches the 'parent' post/thread it is making a comment on
Unfortunately this is all in the same table.. and seems like it would have been better served in a multiple table layout or something?
But:
I know the parent posts (because the subid col is empty)
I know the child posts (because the subid col has value matching the parent post is a comment to)
All posts have a timestamp.
How can I get a query, and group/order things like:
Select -parent posts- without subid
immediately have all -child/comment posts- under this
And have it (initially?) order by the parent post_date then child/comment post (sub order by their post_dates)
Here is an example table set-up:
http://sqlfiddle.com/#!9/19dae/2/0
Not sure if a simple GROUP BY is needed here? Or a more advanced sub-select needs to be done?
The order needs to be 'first' done by parent post 'post_date'.. and (somehow) get the next child/comment posts to be -injected- after their 'parent' post in the query/array return..
Here is my desired outcome order:
Desired outcome:
'2020-03-30 09:48:50', 'Poster A', '49203-30032020094850000000', ''
'2020-03-30 09:50:50', 'Poster B', '49204-30032020095050000000', '49203-30032020094850000000'
'2020-04-03 09:55:50', 'Poster C', '49205-03042020095550000000', '49203-30032020094850000000'
'2020-03-31 09:48:50', 'Poster C', '49205-31032020094850000000', ''
'2020-04-03 09:40:50', 'Poster B', '49204-03042020094050000000', '49206-010432020094850000000'
'2020-04-01 09:48:50', 'Poster D', '49206-010432020094850000000', ''
'2020-04-02 09:49:50', 'Poster E', '49207-02042020094950000000', ''
update:
Addressing: #Ngo Van Quan
For his answer.
This:
select * from testtable
order by
(
CASE testtable.subid WHEN 0
THEN testtable.messageid*1000
ELSE testtable.subid*1000 + testtable.messageid END
),
testtable.messageid;
(which I think it what you intended to supply instead of what you posted??)
** does nothing
This query won't give you exactly what you're looking for, but will give you at least one row for every post. If there are no comments, you'll get one row with a bunch pf null info for comments. If there are comments you'll get one row per comment with the parent post information repeated:
SELECT *
FROM testtable AS post
LEFT JOIN testtable as `comment` ON `comment`.subid = post.messageid
WHERE post.subid = ''
ORDER BY post.postdate ASC;
Here is sql to do your tasks:
select * from table order by (CASE table.subid when 0 then table.id*1000 ELSE table.subid*1000+table.id END), table.id;
Related
Initially, I had an enum list and this sorting was written for it (everything works well)
->orderByRaw("CASE state
WHEN 'Open' THEN 1
WHEN 'Active' THEN 2
WHEN 'Closed' THEN 3
ELSE 4
END")
This was an enum list of project states
How do I make the code above work? So that it works with another table (states), also sorting.
I have projects table that contains state_id column and I have states table that contains id and state_name
I know I can set the correct order in the states table
and then sort - - - > order By ('state_id'). However, this is wrong, I think.
Easy solution is to put a new field named priority to your states table which is an unsigned tiny integer then you can easily sort by priority field
$projects = Project::join('states', 'states.id', '=', 'projects.state_id')
->orderBy('states.priority', 'asc')->select('projects.*')->get();
dd($projects->toArray());
and if you insist to sort by states.state_name you can do this
$projects = Project::join('states', 'states.id', '=', 'projects.state_id')
->orderByRaw("CASE states.state_name
WHEN 'Open' THEN 1
WHEN 'Active' THEN 2
WHEN 'Closed' THEN 3
ELSE 4
END")->select('projects.*')->get();
dd($projects->toArray());
I recommend using ORDER BY FIELD
$value = DB::table('projects')->join('states', 'projects.state_id', 'states.id')
->where('states.state_name', '!=', 'Process')
->orderByRaw("FIELD(state_name,'Open','Active','Closed')")
->paginate(7);
I need some help with a query as I don't seem to get my head around it.
First table vacancies:
vac_id
vac_title
vac_location
vac_description
is_deleted
status
Second table vacancies_labels:
vac_id
Label_id
Now I would like to get an output containing all vacancies within a certain location but they also cannot contain the label_id '10' nonetheless of the location.
SELECT `v`.*
FROM `vacancies` AS `v`
LEFT JOIN `vacancies_labels` as `vl` ON `v`.`vacancy_id` = `bl`.`vacancy_id`
WHERE `v`.`vac_location` = 'russia'
AND `v`.`is_deleted` != 1
AND `v`.`status` = 1
AND `vl`.`label_id` NOT IN ('10')
GROUP BY `v`.`vacancy_id`
This results only in the vacancies that have a record in the vacancies_labels table that are not 10. It leaves out however all vacancies that have no records at all in the vacancies_labels table but fit within the location range.
What am I missing here?
Thx!
Using a LEFT JOIN, if the record is not found, then the values will return null. But in your WHERE clause, you have
AND `vl`.`label_id` NOT IN ('10')
as NOT IN doesn't consider nulls you have to do something like...
AND ( `vl`.`label_id` NOT IN ('10') OR `vl`.`label_id` IS NULL)
I am creating a multisite CMS with codeigniter and am having trouble with a query for navigation links. Each page can have meta values that override the defaults (location_id = 0) with ones specific to the site if set. I have two tables cms_pages and cms_page_meta storing the information. Below is the relevant data
Table cms_pages
page_id
Table cms_page_meta
meta_id
location_id
page_id
tag_name
tag_value
I would like to do this in one call if feasible.
This is the call I was using, it causes pages to be listed twice if a title value exists for all locations (location_id = 0) and a specific location (location_id = x) where I just want the specific location if it exists otherwise the generic, otherwise NULL
SELECT cms_page.*, cms_page_meta.tag_value
FROM cms_pages
LEFT JOIN cms_page_meta ON cms_page_meta.page_id = cms_pages.page_id
WHERE cms_page_meta.location_id IN (0, x)
ORDER BY cms_pages.page_order`
I'm pretty sure I should to use coalesce but just can't figure out how to implement it. Am I trying to do too much at once? Should I split the calls and process with PHP array_merge? I'd like to avoid that as I can cache the call and delete/regenerate the cache whenever I make a change to the pages or metadata. The server gets ~10,000 hits a day so speed is helpful but not necessary yet...
I changed the above query so it only grabs generic data for right now. Any help is greatly appreciated!
I think you should use something like this:
SELECT
cms_page.*,
COALESCE(cms_pm2.meta_id, cms_pm1.meta_id) as meta_id,
COALESCE(cms_pm2.tag_name, cms_pm1.tag_name) as tag_name,
COALESCE(cms_pm2.tag_value, cms_pm1.tag_value) as tag_value
FROM
cms_pages LEFT JOIN cms_page_meta cms_pm1
ON cms_pm1.page_id = cms_pages.page_id
AND cms_pm1.location_id = 0
LEFT JOIN cms_page_meta cms_pm2
ON cms_pm2.page_id = cms_pages.page_id
AND cms_pm2.location_id = x
ORDER BY cms_pages.page_order
I think I found a solution
SELECT cms_pages.*, coalesce(s.tag_value, g.tag_value, 'Coming Soon!') as tag_value
from cms_pages
left join cms_page_meta s on ad_pages.page_id = s.page_id and s.tag_name = "title" and s.location_id = 255
left join cms_page_meta g on ad_pages.page_id = g.page_id and g.tag_name = "title" and g.location_id = 0
WHERE `cms_pages`.`page_group_id` = '5'
AND `page_type` = 1
ORDER BY `cms_pages`.`page_order`
Is this good performance-wise?
I have two table named test and result.
If candidates register then it will be inserted in test table.
If candidate completed test then details has been inserted into result table.
Now I need a query to compare two table and populate detail candidates those who are not complete the test by using candidate id. I am using zend frame work please guide me
I am using this code but it does not work
$dbTableInfo = $this->getDbTable()->info();
$select->from(array(
'c1' => 'test'),
array('c1.candidate_id',
"CONCAT( c1.first_name,
' ',
c1.last_name ) AS full_name",
'c1.active',
'c1.sendlink',
'c1.date_added',
'c1.username',
'c1.date_modified',
'c1.test_id')
);
$select->joinLeft(array('re'=>'result'));
$select->where("c1.business_id='" . $cid . "' AND 'c1.candidate_id NOT IN(re.candidate_id)'");
$select->order('c1.candidate_id');
Sorry for not answering your question, but why don't you simply use a single table with a discriminator field, which would be called type for example, and use it this way:
If a candidate registers he will be inserted with type set to test
If a candidate completed the tests then his type would be changed to completed
Maybe you forgot to specify condition In joinLeft?
from zend docs :
LEFT JOIN with the joinLeft(table, condition, [columns]) method. All
rows from the left operand table are included, matching rows from the
right operand table included, and the columns from the right operand
table are filled with NULL if no row exists matching the left table.
and in your query you didnt specify neither the condition nor the columns you want returned.
for example you can do your join like this :
$select->from(array(
'c1' => 'test'),
array('c1.candidate_id',
"CONCAT( c1.first_name,
' ',
c1.last_name ) AS full_name",
'c1.active',
'c1.sendlink',
'c1.date_added',
'c1.username',
'c1.date_modified',
'c1.test_id')
);
$select->joinLeft(
array('re'=>'result'),
'"c1".candidate_id = "re".candidate_id',
"*"
);
HI all,
I am trying to figure out how to put this into words even, but I am wanting to know how to format the output from each table separately in a "multiple table" mysql query. The output from the table1 "wall" is formatted within a while loop, but the content from table2 "actions" is already formatted(as 1 line of text with links) before it is inserted into the table(column action_body), so inside the loop I would only be outputting the action_date and action_body columns from the actions table.
I am probably not using the correct sql method(if Im doing anything right at all, that is) for the results I need, so feel free to correct my novice example, or suggest a new way to approach this.
Query:
$query = "SELECT wall.wall_id, wall.wall_owner_id, wall.wall_user_id,
wall.wall_post_date, wall.wall_post_content, actions.action_id,
actions.action_date, actions.action_user_id, actions.action_title,
actions.action_body FROM wall, actions
ORDER BY wall.wall_post_date, actions.action_date DESC";
$result = mysql_query($query);
while( $rows = mysql_fetch_assoc($result) {
// What to put here
}
Any help is appreciated, thanks, Lea
Update after comments
SELECT w.* FROM (
(SELECT
'w' as type,
wall_id as id,
wall_owner_id as owner_id,
wall_user_id as user_id,
wall_post_date as post_date,
NULL as title,
wall_post_content as content
FROM wall
WHERE wall_owner_id = x # user id of owner
)
UNION
(SELECT
'a' as type,
action_id as id,
action_user_id as owner_id,
NULL as user_id,
action_post_date as post_date,
action_title as title,
action_body as content
FROM actions
WHERE action_user_id = x # user id of owner
)
) w
ORDER BY w.post_date DESC
Because you don't JOIN on a specific field, you're gonna get every row-row combination of the two tables, which is a whole lot more data than you probably want.
You'd be better of by doing 2 queries, one for each table. While looping through the result of each table, you can collect the data you want in one array, with the field you want to sort it by as array key.
Then you sort the array, and loop through it to print it out.