JOIN; only one record please! - sql

OK, I have a complicated query from a poorly designed DB... In one query I need to get a count from one database, information from another with a link from another, here goes:
Each blog has a type (news, report etc) and a section Id for a certain part of the site but it also can be linked to multiple computer games and sections)
type ( blog_id, title, body, etc...) // yes I know the type is the name of the blog and not just an id number in the table not my design
blog_link ( blog_id, blog_type, section_id, game_id )
blog_comments (blog_id, blog_type, comment, etc...)
So the query goes a little like this:
SELECT bl.`blog_id`, count(bc.`blog_id`) AS 'comment_count', t.`added`
FROM blog_link bl
JOIN type t ON t.`id` = bl.`blog_id`
JOIN blog_comments bc ON (`item_id` = bl.`blog_id` AND `blog_type` = '[$type]')
WHERE bl.`section_id` = [$section_id] AND bl.`blog_type` = '[$type]'
GROUP BY bl.`blog_id`
ORDER BY `added` DESC
LIMIT 0,20
Now this is fine so long as I do not have multiple games associated with one blog.
Edit: So currently if more than one game is associated the comment_count is multiplied by the amount of games associated... not good.
I have no idea how I could do this... It just isn't working! If I could somehow group by the blog_id before I join it would be gold... anyone got an Idea?
Many thanks in advance
Dorjan
edit2: I've offered a bounty as this problem surely can be solved!! Come on guys!

It seems like you just want to get a DISTINCT count, so just add DISTINCT inside the count. Although you will need to add some sort of unique identifier for each comment. Ideally you would have a unique id (ie. auto increment) for each comment, but if you don't you could probably use blog_id+author+timestamp.
SELECT bl.`blog_id`, count(DISTINCT CONCANT(bc.`blog_id`,bc.`author`,bc.`timestamp`) AS 'comment_count',...
That should give you a unique comment count.

I think you need to get the blogs of type "X" first, then do a count of comments for those blogs.
SELECT
EXPR1.blog_id,
count(bc.`blog_id`) AS 'comment_count'
FROM
(
SELECT
bl.blog_id, t.added
FROM
blog_link bl
JOIN
type t ON t.id = bl.blog_id
WHERE
bl.`section_id` = [$section_id]
AND
bl.`blog_type` = '[$type]'
GROUP BY
bl.`blog_id`
ORDER BY
`added` DESC
LIMIT 0,20
) AS EXPR1
JOIN
blog_comments bc ON
(
bc.item_id = EXPR1.blog_id
)

Not tested :
SELECT bl.`blog_id`, count(bc.`blog_id`) AS 'comment_count', t.`added`
FROM
(
SELECT DISTINCT blog_id, blog_type
FROM blog_link
WHERE
`section_id` = [$section_id]
AND `blog_type` = '[$type]'
) bl
INNER JOIN blog_comments bc ON (
bc.`item_id` = bl.`blog_id` AND bc.`blog_type` = bl.`blog_type`
)
INNER JOIN type t ON t.`id` = bl.`blog_id`
GROUP BY bl.`blog_id`
ORDER BY t.`added` DESC
LIMIT 0,20

Related

SQL - Returning fields based on where clause then joining same table to return max value?

I have a table named Ticket Numbers, which (for this example) contain the columns:
Ticket_Number
Assigned_Group
Assigned_Group_Sequence_No
Reported_Date
Each ticket number could contain 4 rows, depending on how many times the ticket changed assigned groups. Some of these rows could contain an assigned group of "Desktop Support," but some may not. Here is an example:
Example of raw data
What I am trying to accomplish is to get the an output that contains any ticket numbers that contain 'Desktop Support', but also the assigned group of the max sequence number. Here is what I am trying to accomplish with SQL:
Queried Data
I'm trying to use SQL with the following query but have no clue what I'm doing wrong:
select ih.incident_number,ih.assigned_group, incident_history2.maxseq, incident_history2.assigned_group
from incident_history_public as ih
left join
(
select max(assigned_group_seq_no) maxseq, incident_number, assigned_group
from incident_history_public
group by incident_number, assigned_group
) incident_history2
on ih.incident_number = incident_history2.incident_number
and ih.assigned_group_seq_no = incident_history2.maxseq
where ih.ASSIGNED_GROUP LIKE '%DS%'
Does anyone know what I am doing wrong?
You might want to create a proper alias for incident_history. e.g.
from incident_history as incident_history1
and
on incident_history1.ticket_number = incident_history2.ticket_number
and incident_history1.assigned_group_seq_no = incident_history2.maxseq
In my humble opinion a first error could be that I don't see any column named "incident_history2.assigned_group".
I would try to use common table expression, to get only ticket number that contains "Desktop_support":
WITH desktop as (
SELECT distinct Ticket_Number
FROM incident_history
WHERE Assigned_Group = "Desktop Support"
),
Than an Inner Join of the result with your inner table to get ticket number and maxSeq, so in a second moment you can get also the "MAXGroup":
WITH tmp AS (
SELECT i2.Ticket_Number, i2.maxseq
FROM desktop D inner join
(SELECT Ticket_number, max(assigned_group_seq_no) as maxseq
FROM incident_history
GROUP BY ticket_number) as i2
ON D.Ticket_Number = i2.Ticket_Number
)
SELECT i.Ticket_Number, i.Assigned_Group as MAX_Group, T.maxseq, i.Reported_Date
FROM tmp T inner join incident_history i
ON T.Ticket_Number = i.Ticket_Number and i.assigned_group_seq_no = T.maxseq
I think there are several different method to resolve this question, but I really hope it's helpful for you!
For more information about Common Table Expression: https://www.essentialsql.com/introduction-common-table-expressions-ctes/

SQL Access 2007/2010 Selecting Max Date with Distinct ID

I believe a lot of people have already asked this question as I have read all the topic from here. But the Problem is I have 3 related tables instead of 2 and I'm not sure how to code for that
I have a table: tbl_Instruments, tbl_Record and tbl_Cal_By.
tbl_Instruments has all the instruments information including their ID.
tbl_Cal_By has the information for whoever is calibrating the tool.
tbl_Records has all the instruments Records and their Calibration date. It inherits the ID from tbl_Instruments as Inst_ID and the Name from tbl_Cal_By as Name_ABBR.
tbl_Instruments: ID, Type
tbl_Cal_By: Cal_ID, Name_ABBR
tbl_Records: Record_ID, Inst_ID, Cal_Date, Name_ABBR
Here is my code.
SELECT tbl_Records.Inst_ID
,tbl_Instruments.Type
,Max(tbl_Records.Cal_Date) AS MaxOfCal_Date
,tbl_Cal_By.Name_ABBR
FROM tbl_Cal_By
RIGHT JOIN (
tbl_Instruments INNER JOIN tbl_Records ON tbl_Instruments.ID = tbl_Records.Inst_ID
) ON tbl_Cal_By.ID = tbl_Records.BY
GROUP BY tbl_Records.Inst_ID
,tbl_Instruments.Type
,tbl_Cal_By.Name_ABBR;
Desired result:
Any help will be appreciated!
You can do this in several methods, one of the is exists :
SELECT tbl_Records.Inst_ID
,tbl_Instruments.Type
,tbl_Records.Cal_Date AS MaxOfCal_Date
,tbl_Cal_By.Name_ABBR
FROM tbl_Cal_By
RIGHT JOIN (
tbl_Instruments INNER JOIN tbl_Records ON tbl_Instruments.ID = tbl_Records.Inst_ID
) ON tbl_Cal_By.ID = tbl_Records.BY
WHERE NOT EXISTS(SELECT 1 FROM tbl_Records t
WHERE t.Inst_ID = tbl_Instruments.ID
AND t.Cal_date > tbl_Records.Cal_Date)
I'm not entirely sure about access syntax and aliases.. so maybe you will have to adjust it a little bit - like alias the first tbl_records so it will recognize it, or maybe it will work..

Select Count of one table into another

I have one SQL statement as:
SELECT ARTICLES.NEWS_ARTCL_ID, ARTICLES.NEWS_ARTCL_TTL_DES,
ARTICLES.NEWS_ARTCL_CNTNT_T, ARTICLES.NEWS_ARTCL_PUB_DT,
ARTICLES.NEWS_ARTCL_AUTH_NM, ARTICLES.NEWS_ARTCL_URL, ARTICLES.MEDIA_URL,
ARTICLES.ARTCL_SRC_ID, SOURCES.ARTCL_SRC_NM, MEDIA.MEDIA_TYPE_DESCRIP
FROM
RSKLMOBILEB2E.NEWS_ARTICLE ARTICLES,
RSKLMOBILEB2E.MEDIA_TYPE MEDIA,
RSKLMOBILEB2E.ARTICLE_SOURCE SOURCES
WHERE ARTICLES.MEDIA_TYPE_IDENTIF = MEDIA.MEDIA_TYPE_IDENTIF
AND ARTICLES.ARTCL_SRC_ID = SOURCES.ARTCL_SRC_ID
AND ARTICLES.ARTCL_SRC_ID = 1
ORDER BY ARTICLES.NEWS_ARTCL_PUB_DT
Now I need to combine another SQL statement into one which is:
SELECT COUNT ( * )
FROM RSKLMOBILEB2E.NEWS_LIKES LIKES, RSKLMOBILEB2E.NEWS_ARTICLE ARTICLES
WHERE LIKES.NEWS_ARTCL_ID = ARTICLES.NEWS_ARTCL_ID
Basically I have one table which contains articles and I need to include the user likes which is in another table.
Use a subquery to add the likescount in your first query like this:
SELECT ARTICLES.NEWS_ARTCL_ID
,ARTICLES.NEWS_ARTCL_TTL_DES
,ARTICLES.NEWS_ARTCL_CNTNT_T
,ARTICLES.NEWS_ARTCL_PUB_DT
,ARTICLES.NEWS_ARTCL_AUTH_NM
,ARTICLES.NEWS_ARTCL_URL
,ARTICLES.MEDIA_URL
,ARTICLES.ARTCL_SRC_ID
,SOURCES.ARTCL_SRC_NM
,MEDIA.MEDIA_TYPE_DESCRIP
,(
SELECT COUNT(*)
FROM RSKLMOBILEB2E.NEWS_LIKES LIKES
WHERE LIKES.NEWS_ARTCL_ID = ARTICLES.NEWS_ARTCL_ID
) AS LikesCount
FROM RSKLMOBILEB2E.NEWS_ARTICLE ARTICLES
,RSKLMOBILEB2E.MEDIA_TYPE MEDIA
,RSKLMOBILEB2E.ARTICLE_SOURCE SOURCES
WHERE ARTICLES.MEDIA_TYPE_IDENTIF = MEDIA.MEDIA_TYPE_IDENTIF
AND ARTICLES.ARTCL_SRC_ID = SOURCES.ARTCL_SRC_ID
AND ARTICLES.ARTCL_SRC_ID = 1
ORDER BY ARTICLES.NEWS_ARTCL_PUB_DT;
I'm not sure what you are trying to achieve but it seems you want to count all the data from 2 tables. You can edit your query to something like this.
SELECT COUNT (ARTICLES.*) FROM RSKLMOBILEB2E.NEWS_LIKES LIKES
JOIN RSKLMOBILEB2E.NEWS_ARTICLE ARTICLES
ON LIKES.NEWS_ARTCL_ID = ARTICLES.NEWS_ARTCL_ID
I think that solution is in using Analytic Functions. Please have a look on https://oracle-base.com/articles/misc/analytic-functions
Please check following query (keep in mind I have no idea about your table structures). Due to left join records might be duplicated, this is why grouping is added.
SELECT ARTICLES.NEWS_ARTCL_ID, ARTICLES.NEWS_ARTCL_TTL_DES,
ARTICLES.NEWS_ARTCL_CNTNT_T, ARTICLES.NEWS_ARTCL_PUB_DT,
ARTICLES.NEWS_ARTCL_AUTH_NM, ARTICLES.NEWS_ARTCL_URL, ARTICLES.MEDIA_URL,
ARTICLES.ARTCL_SRC_ID, SOURCES.ARTCL_SRC_NM, MEDIA.MEDIA_TYPE_DESCRIP,
count(LIKES.ID) over ( partition by ARTICLES.NEWS_ARTCL_ID ) as num_likes
FROM RSKLMOBILEB2E.NEWS_ARTICLE ARTICLES
join RSKLMOBILEB2E.MEDIA_TYPE MEDIA
on ARTICLES.MEDIA_TYPE_IDENTIF = MEDIA.MEDIA_TYPE_IDENTIF
join RSKLMOBILEB2E.ARTICLE_SOURCE SOURCES
on ARTICLES.ARTCL_SRC_ID = SOURCES.ARTCL_SRC_ID
LEFT JOIN RSKLMOBILEB2E.NEWS_LIKES LIKES
ON LIKES.NEWS_ARTCL_ID = ARTICLES.NEWS_ARTCL_ID
WHERE
ARTICLES.ARTCL_SRC_ID = 1
group by ARTICLES.NEWS_ARTCL_ID, ARTICLES.NEWS_ARTCL_TTL_DES,
ARTICLES.NEWS_ARTCL_CNTNT_T, ARTICLES.NEWS_ARTCL_PUB_DT,
ARTICLES.NEWS_ARTCL_AUTH_NM, ARTICLES.NEWS_ARTCL_URL, ARTICLES.MEDIA_URL,
ARTICLES.ARTCL_SRC_ID, SOURCES.ARTCL_SRC_NM, MEDIA.MEDIA_TYPE_DESCRIP
ORDER BY ARTICLES.NEWS_ARTCL_PUB_DT
I also changed coma-separated list of tables from where condition to joins. I think this is more readable since table join conditions are separated from result filtering in where clause.

Access data source using SQL to show most recent entry per site

First of all I am a complete beginner to SQL and have been thrown in at the deep end a bit ! I'm learning as I go along and each mistake I make or question I ask will hopefully help me develop... please be kind :)
I have a working query that extracts electricty meter readings and other information. I am after finding the most recent reading for each site. This is the query at the moment :
PARAMETERS [Site Group] Text ( 255 );
SELECT
Lookup.Lookup_Name AS [Group],
Contacts.Name AS Site,
Points.Number AS MPAN,
Max(DataElectricity.Date) AS MaxDate,
DataElectricity.M1_Present,
DataElectricity.M2_Present,
DataElectricity.M3_Present,
DataElectricity.M4_Present,
DataElectricity.M5_Present,
DataElectricity.M6_Present,
DataElectricity.M7_Present,
DataElectricity.M8_Present,
DataElectricity.Direct
FROM
DataElectricity INNER JOIN (Lookup INNER JOIN (Points INNER JOIN Contacts ON Points.Contacts_Id = Contacts.Id) ON Lookup.Lookup_Id = Contacts.Group_1) ON DataElectricity.Point_Id = Points.Id
WHERE
((DataElectricity.Direct)='D')
GROUP BY
Lookup.Lookup_Name, Contacts.Name, Points.Number, DataElectricity.M1_Present, DataElectricity.M2_Present, DataElectricity.M3_Present, DataElectricity.M4_Present, DataElectricity.M5_Present, DataElectricity.M6_Present, DataElectricity.M7_Present, DataElectricity.M8_Present, DataElectricity.Direct
ORDER BY
Lookup.Lookup_Name, Contacts.Name, Max(DataElectricity.Date) DESC;
However this returns all the readings for a site rather than just the most recent... I'm sure this is simple but I can't figure it out.
Any advice or guidence is gratefully received :)
Can't you just use top 1 to get only the first result?
SELECT top 1 ...
I have evolved the code a bit further using caspian's suggestion of SELECT top 1... but am struggling to refine it further and produce the result I need.
PARAMETERS [Site Group] Text ( 255 );
SELECT
Lookup.Lookup_Name,
Contacts.Name AS Site,
Points.Number AS MPAN,
DataElectricity.M1_Present,
DataElectricity.M2_Present,
DataElectricity.M3_Present,
DataElectricity.M4_Present,
DataElectricity.M5_Present,
DataElectricity.M6_Present,
DataElectricity.M7_Present,
DataElectricity.M8_Present,
DataElectricity.Direct
FROM
(
SELECT TOP 1 DataElectricity.Date AS MaxDate,
DataElectricity.M1_Present,
DataElectricity.M2_Present,
DataElectricity.M3_Present,
DataElectricity.M4_Present,
DataElectricity.M5_Present,
DataElectricity.M6_Present,
DataElectricity.M7_Present,
DataElectricity.M8_Present,
DataElectricity.Point_id
FROM
DataElectricity
ORDER BY MaxDate DESC
)
DataElectricity INNER JOIN (Lookup INNER JOIN (Points INNER JOIN Contacts ON Points.Contacts_Id = Contacts.Id) ON Lookup.Lookup_Id = Contacts.Group_1) ON DataElectricity.Point_Id = Points.Id
WHERE
((Lookup.Lookup_Name)=Lookup_Name)
ORDER BY
Lookup.Lookup_Name, Contacts.Name, MaxDate DESC;
I do have a Google Drive file showing a small example of the data tables and desired result with hopfully a clear guide as to how the tables connect.
https://docs.google.com/file/d/0BybrcUCD29TxWVRsV1VtTm1Bems/edit?usp=sharing
The actual data contains hundreds of Site Groups each with potentially hundreds of sites.
I would like my end users to be able to select the Site Group name from the Lookup.Lookup_Name list and for it to return all the relevant sites and readings.
.... I really hope that makes sense !

SQL query on a condition

I'm writing a query to retrieve translated content. I want it so that if there isn't a translation for the given language id, it automatically returns the translation for the default language, with Id 1.
select Translation.Title
,Translation.Summary
from Translation
where Translation.FkLanguageId = 3
-- If there is no LanguageId of 3, select the record with LanguageId of 1.
I'm working in MS SQL but I think the theory is not DBMS-specific.
Thanks in advance.
This assumes one row per Translation only, based on how you phrased the question. If you have multiple rows per FkLanguageId and I've misunderstood, please let us know and the query becomes more complex of course
select TOP 1
Translation.Title
,Translation.Summary
from
Translation
where
Translation.FkLanguageId IN (1, 3)
ORDER BY
FkLanguageId DESC
You'd use LIMIT in another RDBMS
Assuming the table contains different phrases grouped by PhraseId
WITH Trans As
(
select Translation.Title
,Translation.Summary
,ROW_NUMBER() OVER (PARTITION BY PhraseId ORDER BY FkLanguageId DESC) RN
from Translation
where Translation.FkLanguageId IN (1,3)
)
SELECT *
FROM Trans WHERE RN=1
This assumes the existance of a TranslationKey that associates one "topic" with several different translation languages:
SELECT
isnull(tX.Title, t1.Title) Title
,isnull(tX.Summary, t1.Summary) Summary
from Translation t1
left outer join Translation tX
on tx.TranslationKey = t1.Translationkey
and tx.FkLanguageId = #TargetLanguageId
where t1.FkLanguageId = 1 -- "Default
Maybe this is a dirty solution, but it can help you
if not exists(select t.Title ,t.Summary from Translation t where t.FkLanguageId = 3)
select t.Title ,t.Summary from Translation t where t.FkLanguageId = 1
else
select t.Title ,t.Summary from Translation t where t.FkLanguageId = 3
Since your reference to pastie.org shows that you're looking up phrases or specific menu item names in a table I'm going to assume that there is a phrase ID to identify the phrases in question.
SELECT ISNULL(forn_lang.Title, default_lang.Title) Title,
ISNULL(forn_lang.Summary, default_lang.Summary) Summary
FROM Translation default_lang
LEFT OUTER JOIN Translation forn_lang ON default_lang.PhraseID = forn_lang.PhraseID AND forn_lang.FkLanguageId = 3
WHERE default_lang.FkLanguageId = 1