When joining tables, date column is NULL on some etries - php

I am trying to migrate some custom CMS DB to Wordpress, and so far it's been a living hell.
I am using WP All import plugin, so I need a neat single .csv export that contains data from multiple tables from this custom cms database.
So, these are the columns from two tables that I want to join:
`eo_items`
| cat_id | identificator | create_date |
---------------------------------------------
| 1 | Title of the post | 1283786285 |
`eo_items_trans`
| item_id | lid | name | s_desc | l_desc |
---------------------------------------------------------
| 1 | 33 | Title of the post | excerpt | content |
Desired result should be:
| item_id | lid | name | s_desc | l_desc | cat_id | create_date |
--------------------------------------------------------------------------------
| 1 | 33 | Title of the post | excerpt | content | 1 | Some date |
Here is the script I am using:
SELECT DISTINCT
eo_items_trans.item_id,
eo_items_trans.lid,
eo_items.cat_id,
DATE_FORMAT( eo_items.create_date, '%d.%m.%Y' ) create_date,
eo_items_trans.s_desc,
eo_items_trans.l_desc,
eo_items_trans.name
FROM eo_items_trans
LEFT JOIN eo_items ON ( eo_items_trans.name = eo_items.identificator )
Trouble with this code is that in resulting table some date columns are NULL, and I don't know if the result is what I need because the table has around 2000 rows and I don't know how to cross check if category IDs are correctly populated.
This is the first time I am doing something like this with MySQL so I am really not sure if the procedure is right for what I am trying to achieve.
If you need any clarifications please ask.
EDIT:
eo_items table has some 300 rows more than eo_items_trans so there are some records there that don't have corresponding records in eo_items_trans. I am guessing this should be reflected in the query as well?

Since you're using a LEFT JOIN, NULLs will be returned for any rows of eo_items_trans that do not have entries in oe_items. This could mean the eo_items.identificator is empty, or doesn't exactly match the name (case sensitivity will apply).
You'll have to investigate and clean up the data for rows in eo_items_trans missing the expected row in eo_items.

You NULL results for date seem to come or from eo_items_trans records that have no corresponding entry in the eo_items table or from eo_items records where create_date is null.
You can easily crosscheck check by doing the following
Is there records in eo_items_trans that have no corresponding entries in eo_items:
SELECT DISTINCT eo_items_trans.name FROM eo_items_trans
where NOT EXISTS (
SELECT * FROM eo_items
where eo_items.identificator = eo_items_trans.name
)
If this yields one ore more rows, that will be the eo_items_trans.name records with no correspondent in eo_items. If this is you problem, the do a JOIN, not a LEFT join in your main query
As for empty dates in eo_items you might want to check like this
SELECT * from eo_items WHERE create_date IS NULL
If you find records here, this is where yout NULL values in the main query come from

Related

Left Join and SUM on two tables, MYSQL

I have a table that contains information about an item, and another table that references the owner of that item, like so:
baseItem
--------
itemID | 1 | 2 | 3 | 4 |
itemSize | 5 | 1 | 5 | 3 |
itemCost | 100 | 50 | 1 | 99 |
itemOwner
--------
ownerID | 1 | 1 | 3
itemID | 1 | 4 | 2
What I'm after are the SUMS of itemSize and itemCOST based on the owner. I've looked around but none of the answers I've seen make sense? Here's the best I could come up with, which clearly isn't working:
SUM itemCost FROM baseItem.itemCost LEFT JOIN itemID ON itemOwner.itemid = baseItem.itemid
SELECT ownerId, sum(itemCost) as OwnerCost, sum(itemSize) as OwnerSize
FROM itemOwner
LEFT JOIN baseItem
ON itemOwner.itemid = baseItem.itemid
GROUP BY ownerId
A SELECT statement lists which fields you want to read from the table; in this case you want two values: the id of the owner, and the sum of the values of the items they own. However, since you're using sum (an aggregate function), you must GROUP your elements over some parameter. In this case, you want to group them by ownerId.
A FROM clause references a table; you can start with either baseItem or itemOwner, it makes no difference in this case. You can think of LEFT JOIN as a cartesian product that creates a new table, which contains every element from the cartesian product of both, filtered by the ON clause. However will always have all the items in the left table which is itemOwner and when there are no matching rows in the baseItem all the fields will be NULL. The SUM function will act as if those are 0s and should return you a 0 sum for non matching rows in the baseItem table.
Maybe it is not working because it is invalid SQL statement. Try following code
SELECT SUM(baseItem.itemCost) FROM baseItem
LEFT JOIN itemOwner ON itemOwner.itemId = baseItem.itemId

Returning values even the result are empty

I have a table that is similar below.
| user_id | point_1 | point_2 | point_3
453123 1234 32 433
321543 1 213 321
My query is something like this:
$query = "SELECT * FROM my_table WHERE user_id = 12345 OR user_id = 987654321"
Obviously, this will return nothing since user_id 12345 OR user_id 987654321 do not exist on the table.
But I still want to return something like the one below :
| user_id | point_1 | point_2 | point_3
12345 0 0 0
987654321 0 0 0
You could use an inline view as a rowsource for your query. To return a zero in place of a NULL (which would be returned by the outer join when no matching row is found in my_table, you can use the IFNULL function.
e.g.
SELECT s.user_id
, IFNULL(t.point_1,0) AS point_1
, IFNULL(t.point_2,0) AS point_2
, IFNULL(t.point_3,0) AS point_3
FROM ( SELECT 12345 AS user_id
UNION ALL SELECT 987654321
) s
LEFT
JOIN my_table t
ON t.user_id = s.user_id
NOTE: If datatype of user_id column my_table is character, then I'd enclose the literals in the inline view in single quotes. e.g. SELECT '12345' AS user_id. If the characterset of the column doesn't match your client characterset, e.g. database column is latin1, and client characterset is UTF8, you'd want to force the character strings to be a compatible (coercible) characterset... SELECT _latin1'12345' AS user_id
You can't get the result you want using only a select statement. Only rows that exist somewhere will be returned.
The only way I can think to do this is to insert the query values into a temp table and then outer join against that for your query.
So the basic process would be:
create table temp1 (user_id integer);
insert into temp1 values (987654321); -- repeat as needed for query.
select t.user_id, m.* from temp1 t left outer join my_table m on m.user_id = t.user_id;
drop table temp1;
This isn't very efficient though.
Your desired result resembles the result of an OUTER JOIN - when some records exist only in one table and not the other, an OUTER JOIN will show all of the rows from one of the joined tables, filling in missing fields from the other table with NULL values.
To solve your particular problem purely in SQL, you could create a second table that contains a single field with all of the user_id values that you want to be able to show in your result. Something like:
+-----------+
| user_id |
+-----------+
| 1 |
+-----------+
| 2 |
+-----------+
| 3 |
+-----------+
| ... |
+-----------+
| 12344 |
+-----------+
| 12345 |
+-----------+
| 12346 |
+-----------+
| ... |
+-----------+
And so on. If this second table is named all_ids, you could then get your desired result by modifying your query as follows (exact syntax may vary by database implementation):
SELECT
*
FROM
all_ids AS i
LEFT OUTER JOIN
my_table AS t ON i.user_id = t.user_id
WHERE
i.user_id = 12345
OR i.user_id = 987654321;
This should produce the following result set:
+-----------+----------+----------+----------+
| user_id | point_1 | point_2 | point_3 |
+-----------+----------+----------+----------+
| 12345 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
| 987654321 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
It should be noted that this table full of IDs could take up a significant amount of disk space. An integer column in MySQL can hold 4,294,967,296 4-byte values, or 16 GB of data sitting around purely for your convenience in displaying some other data you don't have. So unless you need some smaller range or set of IDs available, or have disk space coming out your ears, this approach simply may not be practical.
Personally, I would not ask the database to do this in the first place. Essentially it's a display issue; you already get all the information you need from the fact that certain rows were not returned by your query. I would solve the display issue outside of the database, which in your case means filling in those zeroes with PHP.

mysql linked list sort query, why is it working?

My table looks like this:
+------------------------+
| id | title | position |
+------------------------+
| 1 | test 2 | 3 |
+------------------------+
| 2 | test 3 | 1 |
+------------------------+
| 3 | test 1 | 0 |
+------------------------+
I found this query which retrieves the rows ordered based on the position field which holds the id of the predecessor.
SELECT
*
FROM
mytable AS t1
LEFT JOIN
mytable AS t2
ON t2.position = t1.id
I wonder why this is working because there is no order by clause and the database should't know that position 0 is the row to start at.
The result is dependent on the order you inserted the rows into the table. If, for example, you had inserted the row with id=3 before you inserted the row with id=2, then you would have got a non-sorted result.
As it stands, you are pulling the data out of t1 in the order of id because that is the order you put the elements into the table
See http://sqlfiddle.com/#!2/63a925/2 and try it for yourself.
N.B. Databases are not guaranteed to work as you state, it is simply that most databases work this way. You should not rely on this behaviour as a minor change to the schema or query could ruin your whole day! Note also that if id is a (primary?) key, the insert order will probably be overridden by the fact that the database will pull the rows out in the order of the index.
That query is joining in table 2 based on the ID in table 1 equaling the position in table 2. Since the IDs in table 1 are sequential, the output appears to be sorted

SELECT * FROM multiple tables, group by ID

purpose: I have been tasked with exporting a complex dataset from a PHP counseling appointment webapp, and convert it into an excel file containing student data sorted by their STUDENT_ID.
I have 3 MySQL tables containing data. They all have a STUDENT_ID field.
I need to make a query which retrieves all the data from the 4 tables, grouping into a single row based on STUDENT_ID.
Some of the tables contain multiple entries for the same STUDENT_ID. If possible I'd like these multiple entries combined into a single row (so that each unique STUDENT_ID is on one line).
This is what I have so far but it doesn't seem to work how I expect it to.
SELECT *
from ssp_student t1
INNER JOIN ssp_student_quarterly t2
ON t1.STUDENT_ID = t2.STUDENT_ID
INNER JOIN ssp_weekly_progress t3
ON t2.STUDENT_ID = t3.STUDENT_ID
GROUP BY t1.STUDENT_ID
Table Schema:
Table 1:
| STUDENT_ID | PEER_COACH_ID | ACTIVE | COHORT | WEEKLY_MEETING_TIME | FYE_ID | RC | AGREEMENT_SIGNED | RELEASE_SIGNED | NOTES | FACULTY_ADVISOR |
Table 2:
| STUDENT_ID | QUARTER | COUNSELLING_OFFICE | WRITING_CENTER | CASE_MANAGEMENT | SSP_SOCIAL_EVENTS | SSP_SUCCESS_SEMINAR | HOME_SUPPORT | ACCOMODATION_USED | DISCOVERY_PATHWAYS | PEER_COACHING |
Table 3:
| STUDENT_ID | QUARTER | WEEK | EMAIL_INTERACTION | PHONE_INTERACTION | TEXT_INTERACTION | INPERSON_INTERACTION | SOCIAL_INTERACTION | NUMBER_OF_SOCIAL_INTERACTIONS | CASE_MANAGEMENT_INTERACTIONS | NUMBER_OF_CASE_mANAGEMENT_INTERACTIONS | SUCCESS_SEMINAR_INTERACTION | NUMBER_OF_SUCCESS_SEMINAR_INTERACTIONS | OTHER_INTERACTION | THEMES | SURVEY_ID | NOTES |
what I need: I want 1 row for each STUDENT_ID, which contains columns for all the data associated with that STUDENT_ID in tables 1, 2 and 3.
if you use SELECT * and you say that some of the tables contain more than one row for the same student, you will never get only one row. Try to select the fields related to the student id that you want to display.
If any of the fields that you want to display is one of the multiple-entry, then it will not work, it will display one row per entry.
If you really want to concatenate the data for each row into one field, your SELECT statement you could do something like the following:
SELECT t1.STUDENT_ID, GROUP_CONCAT(t2.Field1 SEPARATOR ', ') AS t2Field1Concat,
GROUP_CONCAT(t2.Field2 SEPARATOR ', ') AS t2Field2Concat,
GROUP_CONCAT(t3.Field1 SEPARATOR ', ') AS t3Field1Concat,
GROUP_CONCAT(t3.Field2 SEPARATOR ', ') AS t3Field2Concat
In the above example you would have to do this for each field other than t1.STUDENT_ID.
You seem to be after 4 separate groups of data that have virtually nothing in common other than the student ID. You should perform a single query for each and then combine the data into a relevant format in PHP.
Joining all 4 tables together like this is going to end up with a potentially MASSIVE result set full of duplicate data.

PHP/MySQL - If no rows of a certain type available, load a place holder

So I have this query:
SELECT * FROM cars {$statement} AND deleted = 'no' AND carID NOT IN (SELECT carID FROM reservations WHERE startDate = '".$sqlcoldate."') GROUP BY model
It basically checks the reservations table and then if there are reservations, it gets those carIDs and excludes them from the loop.
This is cool, so as there may be three dodge vipers and 2 are booked out it will only display the last one, and it will only display one at a time anyway because I group the results by model.
All that is good, however when it runs out of entries, so all the cars are booked out, the car does not appear in the list of cars. (As i clear from the query).
I would like a way to say if no rows of a certain car model are in the results, to display a placeholder, that says something like 'UNAVAILABLE'.
Is this possible at all? Its mainly so users can see the company owns that car, but knows its not available on that date.
You should probably handle this in the PHP, checking the number of rows returned and replacing the 0 with "UNAVAILABLE".
Based on TO comment:
In this case you want to look at
http://dev.mysql.com/doc/refman/5.1/en/case.html
This would need to go into the SELECT list like
SELECT
CASE car_count WHEN 0 THEN 'UNAVAILABLE'
WHERE ...
Without seen some of your data, its hard to give you a query, but if you move your subquery to your select expression, you could return the count available (which would be 0 when they are all reserved). Then when you display your data, you could then check if the count is 0, and display your unavailable message.
Edit:
Given the table cars:
+----+----------+
| id | model |
+----+----------+
| 1 | viper |
| 2 | explorer |
| 3 | viper |
| 4 | explorer |
+----+----------+
and the table reservations:
+-------+------------+
| carid | date |
+-------+------------+
| 1 | 2013-03-07 |
| 3 | 2013-03-07 |
+-------+------------+
A query similar to yours above will return:
+----+----------+
| id | model |
+----+----------+
| 2 | explorer |
+----+----------+
If you change it to something like:
SELECT
`outer`.`model`,
(
SELECT COUNT(*)
FROM
`cars` AS `inner`
WHERE
`inner`.`model` = `outer`.`model` AND
`inner`.`id` NOT IN(
SELECT `carid`
FROM `reservations`
WHERE `date` = '2013-03-07'
)
GROUP BY `inner`.`model`
) AS `count`
FROM cars AS `outer`
GROUP BY `outer`.`model`;
then you would get results like:
+----------+-------+
| model | count |
+----------+-------+
| explorer | 2 |
| viper | NULL |
+----------+-------+
If you then needed the NULL value to come back as a 0, you could use COALESCE, as Liv mentioned previously.
It's not pretty, and I'm sure it could be done a much cleaner way, but it does work.
There was a similar question asked here that might get you headed in the right direction. Check out the COALESCE() function.
The built-in function COALESCE() returns the first not-null value in its arguments. This lets you structure queries like SELECT COALSECE(foo, 'bar') [...] such that the result will be the value in column 'foo' if it is not null, or the value 'bar' if it is.

Categories