$query = "SELECT a.comment_user_id as main_id, a.comment_date as timestamp, a.comment_content as content, a.comment_link_id as link_unique, a.comment_id as status, NULL as url, b.user_login as ulogin, b.user_avatar_source as uavatar, c.link_title as ltitle, NULL as desc FROM kliqqi_comments as a WHERE comment_user_id IN ('$following2')
LEFT JOIN kliqqi_users as b ON a.comment_user_id = b.user_id
LEFT JOIN kliqqi_links as c ON a.comment_user_id = c.link_author
ORDER BY timestamp DESC LIMIT 10";
$result = mysqli_query($db_conx, $query);
$row = $result->fetch_array(MYSQLI_ASSOC);
Can anybody tell me what's wrong with the code? It is always returning this error:
Fatal error: Call to a member function fetch_assoc() on boolean
Boolean means this query is not getting executed due to some error in $query variable which I am unable to figure out.
$following is an array. kliqqi_comments alias a, kliqqi_users alias b, kliqqi_links alias c. I am storing all the other fields as alias too. There is no typo or any other silly mistake. I've checked it thoroughly.
UPDATE:
I'm updating this thread because my query actually has many parts and many users may find it helpful.
$query = "SELECT a.comment_user_id as main_id, a.comment_date as timestamp2, a.comment_content as content, a.comment_link_id as link_unique, a.comment_id as status, b.user_login as ulogin, b.user_avatar_source as uavatar, c.link_title as ltitle FROM kliqqi_comments a
LEFT JOIN kliqqi_users b ON a.comment_user_id = b.user_id
LEFT JOIN kliqqi_links c ON a.comment_link_id = c.link_id
WHERE comment_user_id IN ('$following')
UNION ALL
SELECT d.link_author as main_id, d.link_date as timestamp2, d.link_status as content, d.link_id as link_unique, NULL as status, e.user_login as ulogin, e.user_avatar_source as uavatar, d.link_title as ltitle FROM kliqqi_links d
LEFT JOIN kliqqi_users e ON d.link_author = e.user_id
WHERE link_author IN ('$following') AND link_status IN ('new','published')
UNION ALL
SELECT f.vote_user_id as main_id, f.vote_date as timestamp2, f.vote_value as content, f.vote_link_id as link_unique, NULL as status, g.user_login as ulogin, g.user_avatar_source as uavatar, h.link_title as ltitle FROM kliqqi_votes f
LEFT JOIN kliqqi_users g ON f.vote_user_id = g.user_id
LEFT JOIN kliqqi_links h ON f.vote_link_id = h.link_id
WHERE vote_user_id IN ('$following')
ORDER BY timestamp2 DESC LIMIT 30";
What does it do?
I've 3 tables: kliqqi_links, kliqqi_users, kliqqi_votes
UNION ALL
All of them have a timestamp field.
I wanted to fetch contents from these 3 tables combined in decreasing order of timestamp. And to do so, I used UNION ALL (UNION can also be used here but UNION has to run duplicate checks so it's better to avoid it if you can.). But UNION ALL works only when all of the tables have same number of fields. So, I created NULL elements for equating the numbers.
It is to be noted that there is no restriction of datatype for uniting respective fields. But since I had to use timestamp for sequence, I kept them together.
Alias
Since all the respective fields have different names in different tables, I used alias to avoid confusion. Without alias, results are stored in fields mentioned in first SELECT statement which would be a mess.
Multiple LEFT JOIN
Now, I wanted to grab some data from other tables for each SELECT query.
e.g. for kliqqi_comments (first SELECT statement), I wanted to grab user data for the person who made the comment from kliqqi_users plus I wanted to fetch the link where this comment was made from kliqqi_links table. So, I used left join with kliqqi_comments query where comment_user_id from kliqqi_comments equals to user_id from kliqqi_users and comment_link_id from kliqqi_comments equals link_id from kliqqi_links.
Notice that I managed to equate fields in all 3 statements for UNION ALL.
WHERE IN
$following is comma separated array to ensure that it returns result from the people user is following.
ORDER BY DESC, LIMIT
To order by timestamp and LIMIT output result.
That's it.
The where clauses should come after the join clauses, not before them. Additionally, desc and timestamp are reserved words. If you absolutely must use them as a column aliases, you need to escape them:
SELECT a.comment_user_id as main_id,
a.comment_date as `timestamp`, -- Notice the escaping
a.comment_content as content,
a.comment_link_id as link_unique,
a.comment_id as status,
NULL as url,
b.user_login as ulogin,
b.user_avatar_source as uavatar,
c.link_title as ltitle,
NULL as `desc` -- Notice the escaping
FROM kliqqi_comments as a
LEFT JOIN kliqqi_users as b ON a.comment_user_id = b.user_id
LEFT JOIN kliqqi_links as c ON a.comment_user_id = c.link_author
WHERE comment_user_id IN ('$following2') -- Where clause after the joins
ORDER BY `timestamp` DESC LIMIT 10";
Related
I am trying to create an SQL query, but when I left join an additional table (c) with conditions it reduces the number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.NNaziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.id
ORDER BY a.idArtikla ASC
Query below works great. I would like to keep the records I get, but I would like to JOIN another table. Once I do that it reduces number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal
WHERE c.status = 1
ORDER BY a.idArtikla ASC
How I can show all rows from first query and attach table radni_nalozi c with where condition that sums only quantity with status = 1?
try this
With Outer Join, if you put in where clause then it will be treated as inner join.
$stmt = "SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla, norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFTJOIN radni_nalozi c ON a.idArtikla = c.idArtikal and c.status = 1
ORDER BY a.idArtikla ASC";
You outer-join radni_nalozi. This means that in case there is no match, the original row is kept and all radni_nalozi columns are null. Then you limit your results by:
WHERE c.status = 1
This dismisses all outer-joined rows, because their status is null. You have turned your outer join into an inner join.
What you want instead is the condition to be part of the join clause:
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal AND c.status = 1
But as P.Salmon just pointed out, your query is invalid, because of a malformed aggregation. MySQL should raise a syntax error, but obviously you have not SET sql_mode = 'ONLY_FULL_GROUP_BY', which you should in order to have the DBMS prevent you from writing such invalid queries. With sum(c.kolicina) and no GROUP BY clause, you tell the DBMS to limit your results to a single row containing the sum. But what values for a.id, a.naziv, etc. is the DBMS supposed to show then?
It seems you want to join the radni_nalozi sums:
SELECT
ar.id,
ar.naziv as nazivOperacije,
a.Naziv as nazivArtikla,
a.sifra as sifraArtikla,
a.norma,
ar.idArtikla,
rn.sum_kolicina AS kolRadnogNaloga
FROM artikli_rad ar
JOIN artikli a ON a.id = ar.idArtikla
LEFT JOIN
(
SELECT idArtikal, SUM(kolicina) AS sum_kolicina
FROM radni_nalozi
WHERE status = 1
GROUP BY idArtikal
) rn ON rn.idArtikla = ar.idArtikal
ORDER BY ar.idArtikla ASC;
I have also used mnemonic alias names. names like a, b, and c don't make the query more readable as alias names are supposed to do, but make it less readable.
I also turned the outer join on artikli into an inner join, because there must be no artikli_rad row without a match in artikli in a properly set up database.
I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;
I have two tables : activities and activity_images and the foreign key constraint is :
activities.id = activitiy_images.activity_id
Now some activities have images and some do not, which means for a particular activities.id , there might not be an entry on activity_images.
i want to fetch all the data by default , regardless of the data present in activity_images .
Below is my query :
select a.*,a.id as 'activity_id',b.*
from activities a
left join activity_images b on a.id = b.activity_id
where b.image_type='main' LIMIT 0, 9
The issue is with the output . The above query only gives me the data for the matching rows and where b.image_type='main'. It does not output the rows for which there is no entry in activity_images .
I want all the rows but at the same time i want to make sure that b.image_type='main' because there are other values for image_type and i just want to grab the values for image_type= 'main' (If at all there is a match) .
Please advice . Thanks in advance.
select a.*,a.id as 'activity_id',b.*
from activities a
left join activity_images b on a.id = b.activity_id
where b.image_type='main' OR b.image_type IS NULL
LIMIT 0, 9
Would this do what you want?
I would recommend image_type to be a not null column in this case otherwise you could get data that has a matching row but image_type is actually set to null.
When criteria in a WHERE clause applies to a joined table, it's basically treated as an INNER JOIN.
The proper way to handle this is to move the criteria into the ON clause:
SELECT a.*, a.id as 'activity_id', b.*
FROM activities a
LEFT JOIN activity_images b
ON b.activity_id = a.id
AND b.image_type = 'main'
Try a subquery. This will pre-select only the rows in your activity images table with your preferred image type, and will still select everything from your activities table:
SELECT a.*,b*
FROM activities a
LEFT JOIN (
SELECT * FROM activity_images WHERE image_type = 'main'
) b ON a.id = b.activity_id;
SELECT * FROM activity_images WHERE image_type = 'main' a;
I'm executing a query with a LEFT JOIN with an ON clause, but it doesn't appear to be acting correctly.
Relevant schema:
cdrs
id [primary key - auto incrementing]
aparty [VARCHAR(50)]
bparty [VARCHAR(50)]
rates
id [primary key - auto incrementing]
apartypattern [VARCHAR(50)]
bpartypattern [VARCHAR(50)]
There may be multiple patterns that match a cdr. A pattern is considered matched if, reading left to right, all characters of the pattern are present in order from the beginning in the relevant cdr field.
Where mulitple matches are present, the logic to describe the match criteria could be expressed as:
ORDER BY LENGTH(bpartypattern), LENGTH(apartypattern) LIMIT 1
I'm trying to achieve this with the following query:
SELECT c.id, r_a.id
FROM cdrs c
LEFT JOIN (
SELECT id, LENGTH(apartypattern) AS maxlen, apartypattern
FROM rates
) r_a ON c.aparty LIKE CONCAT(r_a.apartypattern,'%')
LEFT JOIN (
SELECT id, LENGTH(bpartypattern) AS maxlen, bpartypattern
FROM rates
) r_b ON c.bparty LIKE CONCAT(r_b.bpartypattern,'%')
WHERE r_a.id = r_b.id
GROUP BY c.id
ORDER BY r_b.maxlen DESC, r_a.maxlen DESC
But the results I'm getting are identical to if I had have replaced the WHERE clause with:
WHERE r_a.id = 1 AND r_b.id = 1
After manual inspection of the data set, there are obvious rows that should have matched that didn't.
As a further troubleshooting step, I tried simplifying the query as follows, still getting results as if that alternative WHERE clause was being applied:
SELECT c.id, r_a.id
FROM cdrs c
LEFT JOIN (
SELECT id, LENGTH(aparty) AS len, apartypattern
FROM rates
) r ON c.aparty LIKE CONCAT(r_a.apartypattern,'%')
SELECT
c.id,
r.id
LENGTH(r.apartypattern) as `r_l`,
LENGTH(r.bpartypattern) as `r_b`
FROM cdrs c
LEFT JOIN rates r ON c.aparty LIKE CONCAT(r.apartypattern,'%') AND
c.bparty LIKE CONCAT(r.bpartypattern,'%')
ORDER BY `r_l` DESC, `r_b` DESC
Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.