SQL Joining tables; can you repeat columns? - sql-server

I'm trying to compose a view that we can use to export our inventory.
I have two tables:
Inventory, which contains the columns Description, Year, Make, Model, and Serial.
Pictures, which contains the columns DocumentBody, MimeType, Serial, and Last Modified.
I'd like to make a view that has all columns from Inventory, and also adds columns for x amount of Pictures related to Serial number.
So if there were two pictures with the same serial number, the resultant table would include these fields:
Description, Year, Make, Model, Serial, DocumentBody1, MimeType1, Last Modified1, DocumentBody2, MimeType2, Last Modified2.
For those Inventory items that only have one picture, the second picture columns would all be null.
Is this something I can even do? From what I'm reading about joins, it doesn't seem possible.

As others have said, you should probably evaluate whether you actually need the view you think you need. But if you really want it, you could use PIVOT in MSSQL:
WITH BaseData AS
(
SELECT Serial
,DocumentBody
,MimeType
,LastModified
,ROWNUMBER() OVER(PARTITION BY Serial ORDER BY LastModified) AS RowNum
FROM Pictures) AS t
),
DocumentPivot AS (
SELECT
Serial
,DocumentBody
,'DocumentBody' + RowNum AS ColumnName
FROM BaseData
),
MimePivot AS (
SELECT
Serial
,MimeType
,'MimeType' + RowNum AS ColumnName
FROM BaseData
),
ModifiedPivot AS (
SELECT
Serial
,LastModified
,'LastModified' + RowNum AS ColumnName
FROM BaseData
)
SELECT Description
,Year
,Make
,Model
,Inventory.Serial
,DocumentBody1
,MimeType1
,LastModified1
,DocumentBody2
,MimeType2
,LastModified2
,...
,LastModified10
FROM Inventory
LEFT OUTER JOIN (
SELECT Serial
,DocumentBody1
,DocumentBody2
,...
,DocumentBody10
FROM DocumentPivot
PIVOT (MAX(DocumentBody) FOR ColumnName IN (DocumentBody1, DocumentBody2, ..., DocumentBody10)) AS P1
) AS Documents
ON Documents.Serial=Inventory.Serial
LEFT OUTER JOIN (
SELECT Serial
,MimeType1
,MimeType2
,...
,MimeType10
FROM MimePivot
PIVOT (MAX(MimeType) FOR ColumnName IN (MimeType1, MimeType2, ..., MimeType10)) AS P2
) AS Mimes
ON Mimes.Serial=Inventory.Serial
LEFT OUTER JOIN (
SELECT Serial
,LastModified1
,LastModified2
,...
,LastModified10
FROM ModifiedPivot
PIVOT (MAX(LastModified) FOR ColumnName IN (LastModified1, LastModified2, ..., LastModified10)) AS P3
) AS Modifieds
ON Modifieds.Serial=Inventory.Serial

Select inventory.*, count(pictures.serial) as picture_count From inventory Left Join pictures On inventory.serial = pictures.serial Where [your where statement]
Use Left Join in case there are no pictures at all. This way you still get back a result.
Update
Actually, after reading your question again, it seems you just want to extend your search results with each additional picture in the system. That's not the best way to do this. The best you can do is just get a row returned for each pic that's in the system.
Select inventory.*, pictures.DocumentBody, pictures.MimeType, pictures.Serial, pictures.Last_Modified From inventory Left Join pictures On inventory.serial = pictures.serial Where [your where statement]
Since there is no "Group By" clause, this will give you 1 row for each picture. Then you can just loop through the results.
Also
There are ways to do this by making temp tables, looping through results within a stored procedure, creating new columns (DocumentBody1, DocumentBody2, etc) for each picture result and adding the data to the new fields, then querying the temp table. But that's a lot to go through I would think.

thanks for the help everyone. In the end, I ended up using PHP to accomplish this with the following code:
<?php
date_default_timezone_set('America/Edmonton');
$serverName = "database";
$connectionInfo = array( "Database"=>"CRM_MSCRM");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false )
{
echo "Unable to connect.\n\n";
die( print_r( sqlsrv_errors(), true));
}
else
{
echo "Connected. Selecting trucks...\n\n";
}
$tsql = "SELECT * FROM CRM_MSCRM.dbo.Trader_Export_Simple";
$stmt = sqlsrv_query( $conn, $tsql);
if( $stmt === false )
{
echo "Error executing query.\n\n";
die( print_r( sqlsrv_errors(), true));
}
$csvData = array();
while ($row = sqlsrv_fetch_array($stmt))
{
$count = 1;
$mainpicsql = "SELECT * FROM CRM_MSCRM.dbo.TruckImages WHERE Serial = '".$row[0]."' AND MainPic = 1";
$mainpicstmt = sqlsrv_query( $conn, $mainpicsql);
while ($mainpicrow = sqlsrv_fetch_array($mainpicstmt))
{
$truck = $mainpicrow[1];
$mainfilename = $truck ."-". $count . ".png";
file_put_contents($mainfilename, base64_decode($mainpicrow[0]));
$mainpicdate = $mainpicrow[3]->format("d/m/Y h:m:s");
$mainfilename = "http://images.website/images/".$mainfilename;
echo $mainpicdate."\n";
}
$picsql = "SELECT * FROM CRM_MSCRM.dbo.TruckImages WHERE Serial = '".$row[0]."' AND MainPic = 0";
$picstmt = sqlsrv_query( $conn, $picsql);
$extrapicsdate = "";
$filenames = "";
while ($picrow = sqlsrv_fetch_array($picstmt))
{
$count++;
$filename = $picrow[1] ."-". $count . ".png";
file_put_contents($filename, base64_decode($picrow[0]));
$picdate = $picrow[3]->format("d/m/Y h:m:s");
$filenames .= "http://images.website/images/".$filename.";";
$extrapicsdate .= $picdate.";";
}
$filenames = rtrim($filenames, ";");
$extrapicsdate = rtrim($extrapicsdate, ";");
echo $filenames."\n";
echo $extrapicsdate."\n";
if ($truck != "") {
$csvData[] = array($truck, $mainfilename, $mainpicdate, $filenames, $extrapicsdate);
}
if ($filenames != "")
{
$filenames = "";
}
if ($extrapicsdate != "")
{
$extrapicsdate = "";
}
echo "Next truck...\n\n";
$truck = "";
$mainfilename = "";
$mainpicdate = "";
}
$fp = fopen('file.csv', 'w');
foreach ($csvData as $fields) {
fputcsv($fp, $fields);
}
//print_r($csvData);
sqlsrv_free_stmt( $stmt);
sqlsrv_free_stmt( $picstmt);
sqlsrv_close( $conn);
?>
this gets the files out but I still have to merge the resultant CSV with the main "information" CSV.

Related

Display Data According to City in CakePHP 3x

I have a business listing site based on CakePHP 3x. I want to display business on the home page based on City Selected. Table Store has [city_id] and Table Cities have [id] in common.
in Header File we got selected city
<option <?php if($selected_city_item == $val['id']){ echo 'selected="selected"'; } ?> value="<?php echo $val['id']; ?>" data-value="<?php echo $val['city_slug']; ?>"><?php echo $val['city'] ?></option><?php } ?>
and we try to filter like this in IndexController
$sqlFeaturedStore = "SELECT s.`id`,s.`biz_name`,s.`biz_slug`,img_1`, st.`state`,c.`city`,c.`city_slug`,a.`area`,ul.id AS ul_id,ul.user_id,ul.store_id,IF(ul.user_id IS NOT NULL , '1', '0') AS isFavourite FROM tbl_stores s LEFT JOIN tbl_states st ON st.id = s.state_id
LEFT JOIN tbl_cities c ON s.`city_id` = c.`id`
LEFT JOIN tbl_areas a ON s.`area_id` = a.`id`
LEFT JOIN tbl_user_likes ul ON s.id = ul.store_id AND ul.user_id = '".$user_id."'
WHERE s.`featured` = '1'
AND s.`status`='1'
AND s.`state_id` = st.`id`
AND s.`city_id` = c.`id`
AND s.`area_id` = a.`id`
AND s.city_id = c.`city` = '" . $selected_city_item . "'
ORDER BY RAND() DESC LIMIT 0,4";
but this not working. Is anyone here to help.

Laravel add values to object based on foreign key

I have a TABLE A called diagnoses . It has a disease_id and a visit_id. I am trying to return the diagnosis from TABLE A along with the disease name and visit details. I keep getting sql syntax errors. Is there a recommended way to push an objects to the array?
My code is
public function diagnosis_diseases(Disease $disease) {
$id = $disease->id;
$items = DB::select(DB::raw('SELECT * FROM diseases WHERE diseases.id = '.$id.' ;'));
foreach($items as $item){
$diagnosis = DB::select(DB::raw(' select * from diagnoses
where disease_id = '. $id.';' ));
$items->push($diagnosis);
}
dd($items);
}
You might need to use an inner join:
public function diagnosis_diseases(Disease $disease) {
$id = $disease->id;
$items = DB::select(DB::raw('SELECT * FROM diseases WHERE diseases.id = '.$id.' ;'));
foreach($items as $item){
$diagnosis = DB::select(DB::raw(' select * from diagnoses
inner join diseases on diagnoses.id = ' .$id' '));
$items->push($diagnosis);
}
dd($items);
}

How to add IN statement with JOIN ON clause in tablegateway ZF2

Getting all result so far until I added a 'IN' statement with join ON clause
My code is something like this:
// $this->table = 'category';
$sql = $this->getSql();
$select = $sql->select();
$select->join('user_category_subscriptions', 'user_category_subscriptions.category_id = category.id AND user_category_subscriptions.status IN (1,2,3,4)', array(), 'left');
$select->where(array('category.user_id = 2 OR (user_category_subscriptions.status IN (2,4))'));
Error
ZEND\DB\ADAPTER\EXCEPTION\INVALIDQUERYEXCEPTION
File:E:\htdocs\myproj\vendor\zendframework\zendframework\library\Zend\Db\Adapter\Driver\Pdo\Statement.php:245
Message:Statement could not be executed
Used 'echo $select->getSqlString();' to print the query:
SELECT "categories".* FROM "categories"
LEFT JOIN "user_category_subscriptions"
ON "user_category_subscriptions"."category_id" = "categories"."category_id" AND "user_category_subscriptions"."status" IN ("1""," "2""," "3""," "4")
WHERE category.user_id = 2 OR (user_category_subscriptions.status IN (2,4))
So the problem is zend's auto converting (1,2,3,4) into ("1""," "2""," "3""," "4")
Any idea to solve this? Thanks
I think you can pass an expression instead of the join string:
// $this->table = 'category';
$sql = $this->getSql();
$select = $sql->select();
$join = new Expression('user_category_subscriptions.category_id = category.id AND user_category_subscriptions.status IN (1,2,3,4)');
$select->join('user_category_subscriptions', $join, array(), 'left');
$select->where(array('category.user_id = 2 OR (user_category_subscriptions.status IN (2,4))'));
try this
$select->where
->AND->NEST->equalTo('category.user_id', 2)
->addPredicate(new Sql\Predicate\In('user_category_subscriptions.status', array(2,4)));

Using concatenated results individually

I got a complicated query which produces a concatenated result. However, although GROUP_CONCAT is necessary for other purposes, I still need to be able to use the pieces of the concatenated string individually.
GROUP_CONCAT(id, name, date AS concat1
echo $db_field['concat1'];
...produces this output: IdNameDate, and I need to be able to use (and echo) Id, Name and Date separately. I guess it must be assigned to an array, I'm a begginer in PHP and I really appreciate any help.
For sake of simplicity, above I have used id instead of eventid, name instead of eventname and date instead of eventstartdate. Below is the full code.
if ($db_found) {
$SQL ="
select sportname,
tournament_templatename,
tournament_stagename,
GROUP_CONCAT(eventid, eventname, eventstartdate SEPARATOR '<br />' ) as concat1
from (
SELECT event.id AS eventid,
event.name AS eventname,
event.tournament_stageFK AS eventtournamentstageFK,
event.startdate AS eventstartdate,
tournament_stage.id AS tournament_stageid,
tournament_stage.name AS tournament_stagename,
tournament_stage.tournamentFK AS tournament_stagetournamentFK,
tournament.id AS tournamentid,
tournament.name AS tournamentname,
tournament.tournament_templateFK AS tournamenttournament_templateFK,
tournament_template.id AS tournamenttemplateid,
tournament_template.name AS tournament_templatename,
tournament_template.sportFK AS tournament_templatesportFK,
sport.id AS sportid,
sport.name AS sportname
FROM
event INNER JOIN tournament_stage ON event.tournament_stageFK=tournament_stage.id
INNER JOIN tournament ON tournament_stage.tournamentFK=tournament.id
INNER JOIN tournament_template
ON tournament.tournament_templateFK=tournament_template.id
INNER JOIN sport ON tournament_template.sportFK=sport.id
WHERE
DATE(event.startdate) = CURRENT_DATE()
) a
group by sportname, tournament_templatename, tournament_stagename
order by sportid, tournament_templatename, tournament_stagename";
$result = mysql_query($SQL);
if($result === FALSE) {
die(mysql_error());
}
while($db_field=mysql_fetch_assoc($result)){
echo $db_field['concat1'];
}
mysql_close($db_handle);
}
I either need a way to get back to results before concatenating them, or a way to display IdNameDate as Id*Name*Date so I can use * as an explode delimiter.
Something like this?
$group = array();
$fields = array();
$fields['id'] = 2;
$fields['name'] = "test";
$fields['date'] = "2010/02/13";
$group["test"] = $fields;
To get an individual field:
echo $group["test"]["id"];
To get a whole group line:
echo implode(",", $group["test"]); // change "," to any delimiter character
To get all group lines:
foreach($group as $key => $g)
echo $key.":".implode(",", $g).";"; // change "," to any delimiter character
Hope this helps...
I guess the only way to do it is to:
Explode the GROUP_CONCAT results to get individual strings
Use substr to extract Id, Name and Date from each result as Id and Date are consistent, they always have the same number of characters
In the end, GROUP_CONCAT does more harm than good.

Counting duplicate entries in database

I want to count the number of times that a zip code is entered into a database. I'm not sure if I am using the right function or not. Also eventually I need to separate the zip codes by the year they were entered int the data base. I know how to separate the years. What I really need help on is counting duplicate entries.
Here is my code.
$sql = 'SELECT * FROM zip ORDER BY time_register';
$result = mysql_query($sql,$db) or die(mysql_error(). "<br />SQL: $sql");
$row = mysql_fetch_array($result);
do{
$visitor_zip= array();
$register = $row['time_register'];
$register_year = date(Y,$register);
print_r(array_count_values($visitor_zip));
} while($row = mysql_fetch_array($result))
Answer: Here is my code that works.
$sql = "SELECT `visitor_zip` AS `zip`, COUNT(`visitor_zip`) AS `cnt`
FROM `zip`
GROUP BY `visitor_zip`
ORDER BY visitor_zip";
$result = mysql_query($sql,$db) or die(mysql_error(). "<br />SQL: $sql");
print '<table class="zip"><tr><td><b>Zip</b></td><td># of</td></tr>';
while($row = mysql_fetch_array($result)) {
print '<tr><td>' . $row['zip'] .'</td><td>'. $row['cnt'] . '</td></tr>';
}
print '</table>';
I think this should work:
SELECT COUNT(*) AS cnt, zipcode FROM zip
GROUP BY zipcode
ORDER BY time_register
select count(ZIPCODEFIELD) as cnt, ZIPCODEFIELD, time_register FROM zip
GROUP BY zipcode,time_register

Resources