Issue with UNION Query - php

I'm trying what seems to be a simple union query for two tables in my database though I am unable to retrieve a successful array result. It should be noted that each table has different columns and that I'd like to create a virtual result (promo AS origin [for the result pulled from the 'venpromo' table] && vacation AS origin [for the result pulled from the 'vacation' table]) in order to sort the results into different array structures. I've looked everywhere and UNIONS are all using different syntax. Thank you all in advance!
<?php
require_once('includes/config.php');
$event_query = "SELECT *, promo AS origin FROM venpromo
UNION
SELECT *, vacation AS origin FROM venpromo
ORDER BY popularity DESC";
$event_result = mysql_query($event_query, $connection);
while ($event = mysql_fetch_array($event_result)) {
if ($event['origin'] == "promo") {
$event_array[] = array(
'id' => $event['id'],
'title' => $event['calname'],
'start' => $event['sdate'],
'end' => $event['edate'],
'color' => "red",
'url' => "http://www.norrisportal.com/bulletinpost.php?id=" . $event['id'] . "&hashkey=akdULjsjyUpYyTzOT7"
);
} elseif ($event['origin'] == "vacation") {
$event_array[] = array(
'id' => $event['id'],
'title' => $event['reason'],
'start' => $event['vacstart'],
'end' => $event['vacend'],
'color' => "blue"
);
}
}
echo json_encode($event_array);
?>
When visiting the page to view the results, I see 'null'.

Put the columns names explicitly rather than *, and make sure the number of columns and data types match for the same column in each select.
I have added some dummy columns to match the number and data types in both the tables and also changed the order of the columns.
try this sample way ::
select
id,
uid,
approval,
vacstart,
vacend,
reason,
'dummy1' col1,
'dummy2' col2,
'dummy3' col3,
curdate() col4,
'dummy4' col5,
'dummy5' col6,
'dummy6' col7,
'dummy7' col8,
promo AS origin
from vacation
union
select
id,
venid,
authid,
sdate,
edate,
authname,
tags,
title,
calname,
date,
intro,
body,
sum,
sortdate,
vacation AS origin
from venpromo;

Related

Group SQL query results by year

I'm new in SQL and I need to group the results of a query by year.
In other words, I have this table structure:
id | title | date
My current query SELECT * FROM table
My current result:
array =>
0 =>
array =>
...
'date' => string '2018-03-09'
1 =>
array =>
...
'date' => string '2018-03-15'
2 =>
array =>
...
'date' => string '2017-03-15'
And I need something like that:
array =>
0 =>
array =>
...
'date' => string '2018-03-09'
array =>
...
'date' => string '2018-03-15'
1 =>
array =>
...
'date' => string '2017-03-15'
Thanks in advance for help :)
[EDIT]
I finally found a way to do what I wanted:
$result = $sql->query("SELECT * FROM abstract ORDER BY date DESC");
$array = [];
forEach($result as $res) {
$year = strtok($res['date'], '-');
if (!isset($array[$year]))
$array[$year][0] = $res;
else
array_push($array[$year], $res);
}
return $array;
If you want group data by year.
Select id,title,date,DATE_FORMAT(date, '%Y') YEAR_GRP from table group by YEAR_GRP
This is something you'll want to do in your query, as MySQL can do it much more efficiently, than PHP.
The way to do it is to add a GROUP BY clause to your query, which will batch rows in your resultset into a single row, based on a grouping value, which in your case will be YEAR(date), as you're looking to group items by the year component in each line's date.
Because you're still looking to have all the results returned, merely grouped by each distinct grouping value, you'll need to use an Aggregate Function to combine all the matching lines into an array of values. Luckily MySQL has a function called json_objectagg which does exactly what you need.
So you'll end up with a query that looks something like:
SELECT year(`date`) AS `year`, json_objectagg(id, `date`, title) AS `line`
FROM abstract
GROUP BY `year`
Instead of json_objectagg, alternatives are e.g. json_arrayagg
SELECT year(`date`) AS `year`, json_arrayagg(id, `date`, title) AS `line`
...or group_concat
SELECT year(`date`) AS `year`, group_concat(id, `date`, title) AS `line`
...depending on what kind of output you require.

How to link and display MySQL data from two different tables?

I have two tables named "stats" and "users"
users table has all the typical user data like id,username,password,email(columns)
stats table has id,attack, defense,ostats,gold,food(columns)
I want to display data from these two tables side by side and have the data linked through their IDS
For example,
Rank user_uid ostats attack defense gold
1 Test 10 5 5 100
2 Test2 8 2 6 60
3 Test3 6 5 1 40
Username is from table "users" and the rest of them are from table "stats"
So first I want to know how to link and display the data from the same ID, like Username(user_id=1) and ostats,attack,defense,gold,food(id=1)
Then I want them in order by their "ostats" (I don't have a column named "rank" in any table yet, just don't know how to create the rank using overall stats)
You could do something like (untested)
SELECT u.username, s.overall, s.attack, s.defense, s.gold
FROM stats s JOIN users u on s.user_uid = u.id
ORDER BY s.overall;
Possible solution to ranking:
set #row_number=0;
SELECT (#row_number:=#row_number+1) as rank, u.username, s.overall, s.attack, s.defense, s.gold
FROM stats s JOIN users u on s.user_uid = u.id
ORDER BY s.overall;
Another, horrible looking attempt:
set #row_number = (select count(*) from users) + 1;
select (#row_number:=#row_number-1) as rank, u.username, s.overall from
stats s join users u on s.user_uid = u.id order by s.overall desc;
set #row_number = 0;
Here in PHP code, you have to run it as two queries to set the variable, then run the actual ranking query. This way, the rank variable is always set to 0 when running this. Note that I've used different table and column names, just to simplify things a little. Remember to adjust to your specific needs.
// connect to database
$conn = mysqli_connect("localhost", "user", "password", "database");
// this query will set a variable to 0.
$setSql = "SET #row_number = 0;";
// run the query. This will return a boolean - true or false, depending on whether or not the query ran successfully
$variableSet = mysqli_query($conn, $setSql);
// if the query ran successfully
if($variableSet){
// setup the actual ranking query
$statsSql = "select
(#row_number:=#row_number+1) as rank,
u.id,
u.username,
s.overall
from
mstats s
join
musers u
on
s.muser = u.id
order by
s.overall desc;";
$ranks = mysqli_query($conn, $statsSql);
if(!$ranks){
// dump error from rank query
var_dump($conn->error);
} else {
// dump results as associative array
var_dump($ranks->fetch_all(MYSQLI_ASSOC));
}
} else {
// dump errors from setting variable
var_dump($conn->error);
}
For me, the results dump looks like this:
array (size=3)
0 =>
array (size=4)
'rank' => string '1' (length=1)
'id' => string '2' (length=1)
'username' => string 'Bar' (length=3)
'overall' => string '1000' (length=4)
1 =>
array (size=4)
'rank' => string '2' (length=1)
'id' => string '6' (length=1)
'username' => string 'Tom' (length=3)
'overall' => string '7' (length=1)
2 =>
array (size=4)
'rank' => string '3' (length=1)
'id' => string '1' (length=1)
'username' => string 'Foo' (length=3)
'overall' => string '3' (length=1)

Fetch all rows related to user

I am trying to create a json object with data from two tables.
Table 1 holds user information
id
name
username
email
Table 2 holds messages to the users
id
f_id
message
date
So far so good. I have managed to do this with LEFT JOIN
$sql = "SELECT * FROM user LEFT JOIN messages ON user.id = messages.f_id
$result = mysqli_query($conn, $sql) or die(mysql_error());
while ($data = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
$messages[] = array(
'users' => array(
'Id' => $data['id'],
'Name' => $data['name'],
'Username' => $data['username'],
'Email' => $data['email'],
),
'messages' => array(
'f_id' => $data['f_id'],
'message' => $data['message'],
'date' => $data['date'],
);
);
}
but the issue/challenge is that it only returns one row from messages table even if there multiple rows related to the user.
This is what I'm trying to achieve
'user' => array(
'id' => 1,
'name' => John Doe,
'username' => Johndoe,
'email' => johndoe#mail.com,
),
'messages' => array(
'id' => 1,
'f_id' => 1,
'message' => 'Just a test',
'date' => 14-08-2014,
'id' => 2,
'f_id' => 1,
'message' => 'Just a test 1',
'date' => 12-08-2014,
);
How can I achieve this?
Do I need to run a while in while?
You can do it in 2 ways:
1) Query only the users, and then iterate over them and query the messages for each user. That means that you will query the database N+1 times, where N is the number of users.
2) Query the messages joined with the users (and not the opposite), which will result with a lot of "unused" data, but a single query.
Try:
SELECT
user.id AS user_id, user.name, user.email, user.username,
messages.id AS message_id, messages.f_id, messages.message, messages.date
FROM user, messages
LEFT JOIN messages ON user.id = messages.f_id
Note that an alias was needed for user.id and messages.id as both column names are the same.
Please update your sql query
$sql = "SELECT * FROM user Right JOIN messages ON user.id = messages.f_id
or
$sql = "SELECT * FROM messages LEFT JOIN user ON user.id = messages.f_id
You can try that :
SELECT * FROM messages LEFT JOIN user ON user.id = messages.f_id
It will get all messages with users but if a user has no messages, you'll not be able to get it with this kind of query.
What is probably happening is that when you're running the query on the users table, it will use the primary key of that table to ensure that there's no "duplicate" data (as it should). You have two options here:
Query Users, Query Messages [Not Recommended]
This might be a little slow since you'll be querying the database many times. You could do something like
SELECT * FROM users;
then later iterate over the users result and run a query on messages
SELECT * FROM messages WHERE f_id = <user_id>;
What's the issue with this? The more users you have, the more wasteful this is, and also the slower your site will perform. Not recommended.
Query messages, join users [Recommended]
Run this query on messages, rather than users. It will use the primary key of the messages table (which will be id) to determine what is a duplicate or not.
SELECT * FROM messages LEFT JOIN users ON users.id = messages.f_id;
This is much more efficient because it's using one query to grab all the data you need and it should need a little tweaking maybe as your site grows.

Counting occurrences of a field based on another field

I'm trying to count a certain field and how much it occurs based on a date. For example, I have a reputation field that I need counted, so if there is 5 reputation points that were individually submitted on 12/14/2014, how would I get the total amount and not just 5 rows?
This is an example of what I am trying to do (if it helps explain it better)
'ID' => 1,
'rep' => 'SK01',
'25/11/14' => '5',
'26/11/14' => '23',
'27/11/14' => '3',
'28/11/14' => '87',
'29/11/14' => '4',
'01/12/14' => '45',
'02/12/14' => '145',
On today [ 2014-12-08 09:53:27 ] there were 1 for AH05 (I'm just getting a repeat of that however many times it is in table) Here is the SQL also if that helps
SELECT *, COUNT(*) as TotalValueCount, DATE(date_created) AS thedate
FROM wp_rg_lead INNER JOIN wp_rg_lead_detail ON wp_rg_lead.id = wp_rg_lead_detail.lead_id
WHERE wp_rg_lead.form_id = '47' AND date_created BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY)
AND NOW() AND field_number = '18' GROUP BY value, date_created
You would do a group by query, but the results would be in rows, not columns:
select rep, date, count(*)
from table t
group by rep, date;
If, for some reason, you really have to have the values in columns, I would suggest doing it at the application layer. Otherwise, Google "MySQL dynamic pivot" to get ideas on what to do.

CakePHP database queries DISTINCT / GROUP BY error

In my CakePHP model I'm trying to get some data from my table.
I tried using DISTINCT but it seems like using DISTINCT doesn't change the query results.
I can see many rows that has the same nick
with 'DISTINCT Mytable.nick'
$this->Mytable->find('all',
array(
'fields'=> array(
'DISTINCT Mytable.nick',
'Mytable.age', 'Mytable.location',
),
'conditions' => array('Mytable.id >=' => 1, 'Mytable.id <=' => 100),
'order' => array('Mytable.id DESC')
));
with 'group Mytable.nick'
$this->Mytable->find('all',
array(
'fields'=> array(
'Mytable.nick',
'Mytable.age', 'Mytable.location',
),
'conditions' => array('Mytable.id >=' => 1, 'Mytable.id <=' => 100),
'group' => 'Mytable.nick',
'order' => array('Mytable.id DESC')
));
with 'Mytable.nick'
$this->Mytable->find('all',
array(
'fields'=> array(
'Mytable.nick',
'Mytable.age', 'Mytable.location',
),
'conditions' => array('Mytable.id >=' => 1, 'Mytable.id <=' => 100),
'order' => array('Mytable.id DESC')
));
Edit: It seems like even CakePHP 2.1 can't use DISTINCT option. When I tried "GROUP BY" it solved my issue. But as you can see from my query I need to order results with Mytable.id descended. When I use GROUP BY, when Mysql finds relevant row, it doesn't take others. For example.
id=1, nick=mike, age=38, location=uk
id=2, nick=albert, age=60, location=usa
id=3, nick=ash, age=42, location=uk
id=4, nick=albert, age=60, location=new_zelland
When I use group Mytable.nick, I don't see 4th row in my results, I see 2nd row. Because when mysql saw "albert" second time, it doesn't put it into my results. But I need latest "albert" result. Is it not possible?
Edit2: It seems like order by/group by conflict is a common problem. I found some tips in this question. But it gives solution for native Mysql queries. I need a solution for CakePHP type queries.
Not clear on why you want to group by nick and order by id. Do you intend to use an aggregate function like COUNT() to see how many occurrences of the same nick there are? In short you overall goal still is not clear to me. Might be worth being aware of the HAVING MySQL keyword.
Updated: Ok, that makes more sense. So you need to use a sub select on the condition or perhaps express that as a join. I'll try and show an example using the sub select in the WHERE clause.
/* select last occurrence for each nick (if you need one for each location )*/
SELECT nick, age, location
FROM myTable t1
WHERE id =
(SELECT MAX(id)
FROM myTable t1
WHERE t1.nick = t2.nick);
Would think something like this would work:
$this->Mytable->find('all',
array(
'fields'=> array(
'Mytable.nick',
'Mytable.age', 'Mytable.location',
),
'conditions' => array('Mytable.id =' => '(SELECT MAX(id) FROM myTable t2 WHERE myTable.nick = t2.nick)', 'Mytable.id <=' => 100)
));

Categories