specifying date to order by in union query - mysql

I'm doing a union of two different tables and ordering by the date column. Shouldn't the union make the two date columns one? It's giving me the error: #1052 - Column 'date' in order clause is ambiguous
Am I missing something here? Here's the query:
SELECT comments.*, postid, prayers.date AS date, prayers.type AS type
FROM comments
LEFT JOIN prayers USING (postid)
WHERE comments.username = 'hoodleehoo'
AND comments.new = 1
AND comments.first = 1
AND (comments.type = 'prayer' or comments.type = 'answer')
AND prayers.privacy != 'hidden'
UNION
SELECT comments.*, postid, posts.date AS date, comments.type AS type
FROM comments
LEFT JOIN posts USING (postid)
WHERE comments.username = 'hoodleehoo'
AND comments.new = 1
AND comments.first = 1
AND (comments.type = 'post' or comments.type = 'shared')
ORDER BY date
UPDATE:
I guess you can't use "date". That must be a reserved word. I changed date to "date2" and it's working fine. I'm sure I'm not the only one who will run into this!

For your query to work as it is now the last line should be ORDER BY posts.date. And by your question i'm thinking you are actually trying to do this
SELECT * FROM ( SELECT comments.*, postid, prayers.date AS DATE2, prayers.type AS type
FROM comments
LEFT JOIN prayers USING (postid)
WHERE comments.username = 'hoodleehoo'
AND comments.new = 1
AND comments.first = 1
AND comments.type = 'prayer'
AND prayers.privacy != 'hidden'
UNION
SELECT comments.*, postid, posts.date AS DATE2, comments.type AS type
FROM comments
LEFT JOIN posts USING (postid)
WHERE comments.username = 'hoodleehoo'
AND comments.new = 1
AND comments.first = 1
AND comments.type != 'prayer')
ORDER BY DATE2

The "ambiguous" messaging is an aliasing issue.
As for ordering the results of a union. It is answered here: How to order by with union
Check Mark Robinson's answer. I think it will suit your needs.

you have to specify tablename with date column
ORDER BY tablename.[date]

Related

Passing argument in LEFT JOIN

I am currently trying to get data from 2 tables with a LEFT JOIN having an unknow value.
I tried using LEFT JOIN but it didn't work.
Here is my code example :
SELECT
cc.shid,
cc.user,
ts.type,
sum(cc.qty1) + sum(cc.qty2) as qty_tot,
COUNT(cc.id) as nb
FROM
content_c cc
LEFT JOIN
(SELECT
s.shid,
s.type
FROM
tab_s s
LIMIT 1
) as ts ON ts.shid = cc.shid
WHERE
cc.time_i like '2019-01%'
GROUP BY
cc.user,
ts.type
With that query it will never work : ts will contain the first occurence of tab_s regardless of cc.shid. I wonder if there is a way to make this :
LEFT JOIN
(SELECT
s.shid,
s.type
FROM
tab_s s
WHERE
s.shid = cc.shid
LIMIT 1
) as ts ON ts.shid = cc.shid
Any idea ? Is there a pointer notion in SQL or something like ? Like I can use &cc.shid, or #cc.shid ?
Note that doing the following :
LEFT JOIN tab_s ts ON ts.shid = cc.shid
Will make my request to take more than 1 minute to display results. And I cannot set an index in tab_s.shid aswell as cc.shid as its have multiple occurences.
Please keep in mind that content_c can have multiple occurence of cc.shid, that why I need to take only the first result (LIMIT 1). It's important.
Use a correlated subquery:
SELECT cc.shid, cc.user, cc.type,
SUM(cc.qty1) + SUM(cc.qty2) as qty_tot,
COUNT(cc.id) as nb
FROM (SELECT cc.*,
(SELECT s.type
FROM tab_s s
WHERE ts.shid = cc.shid
LIMIT 1
) as type
FROM content_c cc
) cc
WHERE cc.time_i >= '2019-01-01' AND
cc.time_i < '2019-02-01'
GROUP BY cc.shid, cc.user, cc.type;
Notes:
The use of LIMIT with no ORDER BY is suspicious. Why would there be duplicates in the underlying table?
Your date comparisons are bad. Use date/time functions when working with date/time values. Don't use string functions.
The GROUP BY should include all non-aggregated columns in the SELECT.
As discussed in the question comments, Can you please try this script and see if it meets your requirements? This will return a row per ID in "content_c" table with the GROUP BY impact.
SELECT
cc.shid,
cc.user,
ts.type,
sum(cc.qty1) + sum(cc.qty2) as qty_tot,
COUNT(cc.id) as nb
FROM content_c cc
LEFT JOIN
(
SELECT DISTINCT s.shid, s.type FROM tab_s s
) AS ts ON ts.shid = cc.shid
WHERE cc.time_i like '2019-01%'
GROUP BY cc.shid,cc.user,ts.type

mysql how to use JOIN instead of IN with WHERE clause

Can anyone please help me with below query in which i use IN clause which Leads performance issue. I want to use JOIN for it but not sure how for do for such query.
select *
from user_followings
where followed_id = 'xyz' AND owner_id IN (
select DISTINCT owner_id
from feed_events
where DTYPE = 'PLAYLIST' AND last_updated_timestamp > '20-04-2017' AND (feed_type = 'PLAYED_PLAYLIST' OR feed_type = 'STARTED_LISTENING')
order by last_updated_timestamp DESC)";
A join probably is not the best approach. Use exists:
select uf.*
from user_followings uf
where uf.followed_id = 'xyz' and
exists (select 1
from feed_events fe
where uf.owner_id = fe.owner_id and
fe.DTYPE = 'PLAYLIST' and
fe.last_updated_timestamp > '2017-04-20' and
fe.feed_type in ('PLAYED_PLAYLIST', 'STARTED_LISTENING')
);
You want an index on feed_events(owner_id, dtype, last_updated_timestamp, feed_type) and user_followings(followed_id, owner_id).
Other notes:
ORDER BY in such a subquery is useless.
Use standard date formats (YYYY-MM-DD) for constant dates.
Use IN instead of a bunch of ORs. It is easier to read and optimizes better under most circumstances.
I rewrote your query using join:
SELECT *
FROM user_followings
INNER JOIN feed_events ON user_followings.owner_id = feed_events.owner_id
WHERE followed_id = 'xyz'
AND DTYPE = 'PLAYLIST'
AND feed_events.last_updated_timestamp > '20-04-2017'
AND (
feed_type = 'PLAYED_PLAYLIST'
OR feed_type = 'STARTED_LISTENING'
)
ORDER BY last_updated_timestamp DESC

Group and order by a column but donot include that column in results

I've been trying to figure out how I can modify this query so that the result set does not include the numHits. I want the same results in the same order, just not have the numHits included.
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
,COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) as numHits
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY numHits DESC;
sample results:
inventoryId, numHits
6928, 2
6929, 2
6924, 2
6925, 2
13772, 2
6926, 2
18203, 1
6931, 1
13863, 1
18402, 1
Desired Results:
inventoryId
6928
6929
6924
6925
13772
6926
18203
6931
13863
18402
Move the column from SELECT clause to ORDER BY clause:
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) DESC;
SELECT
`newel_inventoryKeywordIdDictionaryId`.`inventoryId`
FROM
`newel_inventoryKeywordIdDictionaryId`
, `newel_inventoryDictionary`
WHERE
`newel_inventoryKeywordIdDictionaryId`.`dicId` = `newel_inventoryDictionary`.`dicId`
AND (
`newel_inventoryDictionary`.`word` = 'alabaster' OR `newel_inventoryDictionary`.`word` = 'chess'
)
GROUP BY inventoryId
ORDER BY COUNT(`newel_inventoryKeywordIdDictionaryId`.`inventoryId`) DESC;
You just need to put the aggregation in the ORDER BY. However, you should also:
Use explicit join syntax. Never use commas in the from clause.
Use table aliases. They make queries easier to write and to read.
Use in instead of a bunch of or statements.
Here is an improved version of the query:
SELECT kdi.inventoryId
FROM newel_inventoryKeywordIdDictionaryId kdi JOIN
newel_inventoryDictionary id
ON kdi.dicId = id.dictId
WHERE id.word IN ('alabaster', 'chess')
GROUP BY kdi.inventoryId
ORDER BY COUNT(*) DESC;

LEFT JOIN SUM with WHERE clause

The following query always outputs SUM for all rows instead of per userid. Not sure where else to look. Please help.
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
) assignments ON (userid = assignments.userid AND ticketid = ?)
WHERE ticketid = ?
ORDER BY assigned,scheduled
If you want to keep the SELECT *, you would have to add a group by clause in the subquery. Something like this
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
GROUP BY userid
) time_entriesSummed ON time_entriesSummed.userid = assignments.userid
WHERE ticketid = ?
ORDER BY assigned,scheduled
But a better way would be to change the SELECT * to instead select the fields you want a add a group by clause directly. Something like this
SELECT
assignments.id,
assignments.assigned,
assignments.scheduled,
SUM(time_entries.timeworked) AS totalTimeworked
FROM assignments
LEFT JOIN time_entries
ON time_entries.userid = assignments.userid
GROUP BY assignments.id, assignments.assigned, assignments.scheduled
Edit 1
Included table names in query 2 as mentioned in chameera's comment below

MySQL Inner Join with where clause sorting and limit, subquery?

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.