Using two tables. What am I doing wrong here? - sql

I want to create an activity feed system and my feeds (statuses) and friends are in different tables in my database. How do I connect them so that the logged in user can only receive feeds from their friends.
<?php
$sql = "
SELECT * FROM status WHERE author='(friend of logged-in user)' AND type='a'
**UNION**
SELECT * FROM friends WHERE user1='$user' AND accepted='1' OR user2='$user' AND accepted='1'
";
$query = mysqli_query($database, $sql);
$statusnumrows = mysqli_num_rows($query);
while ($row = mysqli_fetch_array($query, MYSQLI_ASSOC)) {
$user1 = $row["user1"];
$user2 = $row["user2"];
$accepted = $row["accepted"];
$statusid = $row["id"];
$account_name = $row["account_name"];
$author = $row["author"];
$postdate = $row["postdate"];
$postdate = strftime("%b %d, %Y %I:%M %p");
$data = $row["data"];
$data = nl2br($data);
$data = str_replace("&","&",$data);
$data = stripslashes($data);
$statusDeleteButton = '';
if($author == $log_username || $account_name == $log_username ){
$statusDeleteButton = '<span id="sdb_'.$statusid.'">delete status</span> ';
}
$feedlist .= '<div id="status_'.$statusid.'" class="flipwrapper pin">
<div class="picture">
<header class="img-btm">
'.$postdate.'</b><br />
20 cmts 255 likes '.$statusDeleteButton.'
</header>
<img id="bound" src="'.$data.'"/></div></div>';
}
?>

You definitely don't want a UNION here, but a subquery, more or less like this:
SELECT * FROM status
WHERE author in (
SELECT whateverfieldshouldmapfromfriendstoauthor FROM friends
WHERE user1='$user' AND accepted='1' OR user2='$user' AND accepted='1'
) AND type='a'
And some general tips:
developing your queries straight in your scripting language (in this case php) is a very bad idea. Use a tool that allows you to develop queries, run them and inspect the result sets. There are tons of those, like MySQL's own Workbench and Squirrel SQL
be very careful about SQL injection problems (if your $user variable is provided by the request, you are). The best way to avoid SQL injection problems is by using parameterized queries.

Related

Query giving double result instead of single

I have two tables: products and current_product_attribute_values
I have tried a join query to filter them as per attribute selected by the user but when I try this with an additional condition it gives me 2 results instead of one it is including the first one which is not matching as per query:
select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
here is my laravel Controller code :
$all = Input::all();
$q = Input::get('search_text');
$att_val = Input::get('attribute_value');
$subcat = Input::get('subcat_id');
$subcat_name = DB::table('subcategories')->where('id', $subcat)->value('subcategory_name');
$brandname = DB::table('brands')->where('subcat_id', $subcat)->value('brand_name');
$brand_id = DB::table('brands')->where('subcat_id', $subcat)->value('id');
$product_count = DB::table('products')->where('brand_id', $brand_id)->count();
if ($q != "") {
// getting multiple same name params
$query = DB::table('products');
$query->join('current_product_attribute_values', 'products.id', '=', 'current_product_attribute_values.product_id');
$j = 0;
foreach ($all as $key => $values) {
//echo 'my current get key is : ' . urldecode($key). '<br>';
if ($key == $name[$j]) {
$query->where('current_product_attribute_values.attribute_id', '=', $att_id_value[$j]);
echo'<br>';
print_r($query->toSql());
echo'<br>';
//echo '<br> key matched and have some value : <br>';
//echo count($values);
if (count($values) >= 1) {
//echo '<br> it has array inside <br>';
foreach ($values as $val) {
// or waali query in same attribute
echo'<br>';
$query->orwhere('current_product_attribute_values.attribute_value_id', '=', $val);
print_r($query->toSql());
echo'<br>';
}
}
$j++;
}
}
$records = $query->toSql();
$query->where('product_name', 'LIKE', '%' . $q . '%');
$records = $query->toSql();
print_r($records);
$products = $query->paginate(10)->setPath('');
$pagination = $products->appends(array(
'q' => Input::get('q')
));
if (count($products) > 0) {
$filters = DB::table('product_attributes')->where('subcategory_id', $subcat)->get(['attribute_title']);
} else {
$filters = array();
}
$categories = categories::where('add_to_menu', 1)->with('subcategories')->with('brands')->get();
$categoryhome = categories::where('add_to_menu', 1)->with('subcategories')->get();
return view('searchfilter')
->with('productsdata', $products)
->with('filtersdata', $filters)
->with('categories', $categories)
->with('categorieshome', $categoryhome)
->with('subcat_name', $subcat_name)
->with('subcat_id', $subcat)
->with('brandname', $brandname)
->with('product_count', $product_count)
->with('querytext', $q);
}
return 'No Details found. Try to search again !';
its easier if you use raw sql as calling db select function. ex:
$query=DB::select("select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
");
indeed you can concat vars in raw sql if you need to, ex:
$queryBrands = "select id from brands where subcat_id =".$subcat;
//echo $queryBrands
$queryBrands = DB::select($queryBrands);
By looking at your tables, product table with id value 17 has two records in table current_product_attribute_values in column product_id (I assume this column is used as foreign key to product table).
With select *, you select all of the columns from both tables. So it would most likely cause your query to return multiple records.
My suggestions:
Only select the columns you need. Avoid using select * in the long run, i.e. select product.id, product.description, current_product_attribute_values.attribute_values ......
Make use of GROUP BY
Hope these helps.

Pagination with PDO prepare statement

I am using David Carr's pagination class successfully for a whole-table call (i.e. where I'm calling all the rows in the table without filtering). After including my config and the class, I accomplish it this way:
$pages = new Paginator('3','p');
$stmt = $dbh->query('SELECT count(id) FROM sermons');
$row = $stmt->fetch(PDO::FETCH_NUM);
$total = $row[0];
//pass number of records to
$pages->set_total($total);
$results = $dbh->query('SELECT * FROM sermons ORDER BY date_preached DESC '.$pages->get_limit()); ?>
<div class="paging"><p><?php echo $total; ?> sermons found</p> <?php echo $pages->page_links();?></div>
<?php
foreach($results as $row)
Etc.
That works great, but now I'm trying to implement it for the result of user filtering via forms, and because I can't use an unnamed placeholder in the query, I'm trying to do it with a prepare (which I'm guessing is more secure anyway):
$pages = new Paginator('2','p');
$series = isset($_POST['series']) ? $_POST['series'] : false;
try {
$stmt = $dbh->prepare("SELECT count(id) FROM sermons WHERE series = ?");
$stmt->execute(array($series));
$row = $stmt->fetch(PDO::FETCH_NUM);
$total = $row[0];
//pass number of records to
$pages->set_total($total);
$results = $dbh->prepare("SELECT * FROM sermons WHERE series = ? ".$pages->get_limit());
$results->bindParam(1, $series, PDO::PARAM_STR);
$results->execute(array($series));
// the call
echo $total; ?> sermons found</p> <?php echo $pages->page_links();
When I first get to the results page from the form, it looks like everything is working properly. The call correctly identifies the number of sermons found for the search result, and displays the first two (or however many I set in the $pages statement). But when I click to another page result, no sermons display, the count is gone, and my !isset(found_rows) echo returns a zero as well.
I was originally thinking the issue was that at first I was trying to do this without binding. That is questionable, since without the pagination I can display the page without binding. In any case, I've been trying that with both bindValue and bindParam, but with no luck.
Am I mishandling something in the prepare statement? Or what?
Note: I have the results page displaying fine without the pagination with this:
$series = isset($_POST['series']) ? $_POST['series'] : false;
try {
$results = $dbh->prepare("SELECT * FROM sermons WHERE series = ?");
$results->execute(array($series));
// Etc.

SQL Compare Characters in two strings count total identical

So the over all on this is I have two different systems and in both systems I have customers, unfortunately both systems allow you to type in the business name freehand so you end up with the example below.
Column A has a value of "St John Baptist Church"
Column B has a value of "John Baptist St Church"
What I need to come up with is a query that can compare the two columns to find the most closely matched values.
From there I plan to write a web app where I can have someone go through and validate all of the entries. I would enter in some example of what I have done, but unfortunately I honestly dont even know if what I am asking for is even possible. I would think it is though in this day and age I am sure I am not the first one to try to attempt this.
You could try and create a script something like this php script to help you:
$words = array();
$duplicates = array();
function _compare($value, $key, $array) {
global $duplicates;
$diff = array_diff($array, $value);
if (!empty($diff)) {
$duplicates[$key] = array_keys($diff);
}
return $diff;
}
$mysqli = new mysqli('localhost', 'username', 'password', 'database');
$query = "SELECT id, business_name FROM table";
if ($result = $mysqli->query($query)) {
while ($row = $result->fetch_object()) {
$pattern = '#[^\w\s]+#i';
$row->business_name = preg_replace($pattern, '', $row->business_name);
$_words = explode(' ', $row->business_name);
$diff = array_walk($words, '_compare', $_words);
$words[$row->id][] = $_words;
$result->close();
}
}
$mysqli->close();
This is not tested but you need something like this, because I don't think this is possible with SQL alone.
---------- EDIT ----------
Or you could do a research on what the guys in the comment recommend Levenshtein distance in T-SQL
Hope it helps, good luck!

$data = $query->row(); returns only one row

Im trying to list the results of my sql query (picking up all the movies from a category), but I cannot figure out how to get all the rows instead of only one.
Here's the code :
$this->load->database();
$sql = 'SELECT * FROM movies WHERE category = "'.$movies_category.'";';
$query = $this->db->query($sql);
$data = $query->row();
$this->response($data, 200);
I've tried :
while($row = mysql_fetch_assoc($query)){
$data = $query->row();
}
$this->response($data, 200);
And it doesn't work. Any suggestion ? Thank you !
$this->load->database();
$sql = 'SELECT * FROM movies WHERE category = "'.$movies_category.'";';
$query = $this->db->query($sql);
$data = $query->result();
To traverse the $data array:
foreach($data AS $row)
{
//to retrieve the data from each row.
$col1 = $row->col1;
$col2 = $row->col2;
}
Use result() instead of row(). result() will return an array of objects that are your results. Alternatively, you can useresult_array() which will resturn an array of arrays keyed according to your columns. Please refer to here for a better outline of the result() and row() methods.
Do you have a database configuration file? the load->database() requires it. Where is $movies_category coming from? This will let you iterate over your results.
$this->load->database();
$sql = 'SELECT * FROM movies WHERE category = "'.$movies_category.'";';
$query = $this->db->query($sql);
foreach ($query->result() as $row)
{
echo $row->column;
}
Where column corresponds with one of the values in the movies table.
I'm surprised nobody has mentioned the potential hazards of using variables (possibly user input) in your SQL. You should seriously consider using query bindings or the active record features of CodeIgniter to build safer queries.
Consider the following solution to your problem:
$this->load->database();
$sql = 'SELECT * FROM movies WHERE category = ?';
$query = $this->db->query($sql, array($movies_category));
// $data = $query->result(); // returns result as an array of objects
$data = $query->result_array(); // returns result as array
$this->response($data, 200);
I'm assuming this is for some sort of API? If so, consider using the result_array() method as it will probably be better suited for your needed output, and also really easy to convert into JSON:
$json_data = json_encode($data);
Hope that helps,
Cheers.
For your question row() return only one value its good for checking in ID and if you want get all the rows use result_array() or simple result()
You can try this code....
Model:
function get_movies($movies_category){
$this->db->where("category",$movies_category);
$query = $this->db->get("movies");
return $query->result_array();
}
Controller:
$this->data['movies'] = $this->'name of model'->get_movies('here is the movie categories');
View:
foreach($movies as $m){
print_r($m);
}
exit();
Note you can directly add the code in function in model to controller add this in your controller if you want directly...
$this->data['movies] = $this->db->get('movies')->result_array();

How can I get a list of indices on a SQL table using Perl?

How can I get a list of the indices on a table in my sybase database using Perl? The goal is to "copy" all the indices from a table to a nearly identical table.
Is $dbh->selectarray_ref('sp_helpindex $table') the best I can do?
There's a statistics_info() method in DBI, but unfortunately, the only DBD I've seen it implemented in so far is DBD::ODBC . So if you use ODBC (update: or PostgreSQL!) you're in luck. Otherwise sp_helpindex (or the sysindexes table) is about as good as it gets for Sybase.
Here's what I've used for Sybase (in my own OO module - it returns only unique indexes unless the all_indexes argument is true):
{
my $sql_t = <<EOT;
select
sysindexes.name,
index_col(object_name(sysindexes.id), sysindexes.indid, syscolumns.colid) col_name
from sysindexes, syscolumns
where sysindexes.id = syscolumns.id
and syscolumns.colid <= sysindexes.keycnt
and sysindexes.id = object_id(%s)
EOT
sub index_info {
my ( $self, $table, $all_indexes ) = #_;
my $dbh = $self->{DBH};
my $sql = sprintf $sql_t, $dbh->quote($table);
$sql .= "and sysindexes.status & 2 = 2\n" unless $all_indexes;
my $sth = $dbh->prepare($sql);
$sth->execute();
my #col_names = #{$sth->{NAME_lc}};
my %row; $sth->bind_columns(\#row{#col_names});
my %ind;
while ($sth->fetch()) {
if ( $row{col_name} ) {
push #{$ind{$row{name}}}, lc($row{col_name});
}
}
return unless %ind;
return \%ind;
}
}
Or if your goal is to just copy indexes, maybe you should get the dbschema.pl utility (which uses Sybase::DBlib). It will generate the "CREATE INDEX" statements for you.
SELECT i.*
FROM sysobjects o, sysindexes i
WHERE o.name = $name
AND i.id = o.id