Include rows which don't match with IN() clause - mysql

I have a table called log which contains logs sent by several applications. This table has a varchar field called reference.
I have a table panel in Grafana in which I show how many logs we have grouped by reference values. So the user types one or multiple values in a text field on Grafana like 'ref1', 'ref2', 'ref3' and a query like this is fired:
SELECT reference, count(id)
FROM db.log
WHERE reference IN('ref1', 'ref2', 'ref3')
GROUP BY reference
So far so good, it works as intended. What I would like to do is showing a row with count=0 in case a log with given reference doesn't exist. I know I could add arbitrary rows using UNION but I think I can't do it in Grafana dynamically.
Any ideas?

Use a query that returns all the values for which you want results and left join the table to aggregate:
select t.reference, count(l.id)
from (
select 'ref1' reference union all
select 'ref2' union all
select 'ref3'
) t left join db.log l
on l.reference = t.reference
group by t.reference
See a simplified demo.

Related

mysql Create named table from values in select

I have a table, System, with a bunch of fields including System.serial.
I have a list of serial numbers that I want to get the status of.
Simple enough:
Select * from System where System.serial in ('s1','s2', 'sn');
However the list of serial numbers also has serials NOT IN the System table.
Obviously they are not in the results.
I want the missing serials to show in the results also but with no data.
The best way I can think of doing this is to make a temporary table with one column, serial, and then left join System on it.
How can I do this without creating a temporary table?
Something like:
Select listOfSerials.serial, System.*
from (Select ('s1','s2', 'sn') as serial ) as ListOfSerials
left join System on System.serial = ListOfSerials.serial;
Thanks,
Ryan
You're on the right track with your solution of creating a virtual table with which to do LEFT JOIN against your real data.
You can create a derived table as a series of UNIONed SELECT statements that select literal values with no table reference.
SELECT listOfSerials.serial, System.*
FROM (
SELECT 's1' AS serial
UNION SELECT 's2'
UNION SELECT 'sn'
) AS ListOfSerials
LEFT JOIN System ON System.serial = ListOfSerials.serial;
You only need to define a column alias in the first SELECT in the UNION. The rest are required to use that column alias.
Creating a reference table to store the list of serials is probably your best option. That would allow you to write a query like:
SELECT r.serial reference_serial, s.serial system_serial
FROM reference_table r
LEFT JOIN system_table s ON s.serial = r.serial
With the LEFT JOIN, serials declared in the reference table but unavailable in the system table will have second column set to NULL.
A quick and dirty work around is to use UNIONed subqueries to emulate the reference table:
SELECT r.serial reference_serial, s.serial system_serial
FROM (
SELECT 'serial1' AS serial
UNION ALL SELECT 'serial2'
UNION ALL SELECT 'serial2'
...
) r
LEFT JOIN system_table s ON s.serial = r.serial

Fetching value from table and passing it in url

How can i use in table field values in the url
SQL Query wherein all 3 tables are joined
select * from nfojm_usedcar_variants cv
inner join nfojm_usedcar_products cp
inner join nfojm_usedcar_categories cc on
cc.id=cp.prod_cat_id and
cp.id=cv.v_prod_id and
cv.state='1' order by cv.id desc
Output as checked
Then it combines all 3 tables
nfojm_usedcar_variants
nfojm_usedcar_products
nfojm_usedcar_categories
However - all 3 tables have unique field i.e id (but with different values)
I need to pass on value of id and v_prod_id in a url
say url been :-
<a href="index.php?option=com_usedcar&pid='.$row->v_prod_id.'&vid='.$row->id.'">
But id been common field in most of the tables hence its not picking in correctly from nfojm_usedcar_variants,
Can some one help to modify a function so as to fetch in value of id and v_prod_id from the respective table of nfojm_usedcar_variants
thanks
If you have multiple tables in a join that share a common column name, and you need them, then alias them. Such as:
select a.id as aid,a.theName,b.id as bid,b.year
from tableA a
join tableB b
on b.id=a.id
then refer to those columns as aid and bid in your code that follows.
Try to avoid Ever doing a select *. Be explicit. You never know what comes flying out of a select * typically. And odds are you don't need it all. Select * is fine for messing around, but not for production code. And you can't control common column names with select *. We like to control things afterall, no?

Compare 1 row of returned data from multi-row nested SQL statement

I have two tables... EVENT (Primary key is EID) (containing all the general event data) and SINGLE_EVENT (Primary Key is SEID) (containing the information about each individual event related to a particular event ID i.e. date and time of the individual event, location of the venue etc.
In summary I want to find the 'single event' which is happening soonest for each overall event(EID) -- this should return a single event for each unique EID in the SINGLE_EVENTS table
I then want to bind the overall EVENT information to the returned results.
The problem is that, with the current MySQL statement I have below, I need to select * for the nested query to have all the information it needs to process the query but I also DONT want to select all that information because I only need the SEID from that query result (and not the whole table)
here is my query (obviously executed without the comments):
<!-- non working outer query...
SELECT SINGLE_EVENT.SEID, EVENT.* FROM EVENT
INNER JOIN SINGLE_EVENT ON SINGLE_EVENT.EID=EVENT.EID
WHERE SINGLE_EVENT.SEID IN (
-->
<!-- working sub query...
select * from SINGLE_EVENT t
inner join (select eid, min(date) as MinDate from SINGLE_EVENT
group by eid) tm
on t.eid=tm.eid and t.date=tm.MinDate and t.date>=sysdate()
-->
)
I am new to SQL and dont know how best to find this information out from the tables. I feel that I'm very close to it working but I keep getting the message "Operand should contain 1 column(s)" because of the multi-column return value of the sub-query.
Any help is appreciated.
You can simply join first with your aggregates and then with your real table:
select e.*, se.*
from event e
join
(
select eid, min(date) as date
from single_event
where date >= sysdate()
group by eid
) first_events on first_events.eid = e.eid
join single_event se on se.eid = first_events.eid and se.date = first_events.date;
In your subquery, change:
select * from SINGLE_EVENT t
to
select SEID from SINGLE_EVENT t
You're effectively telling it to search the whole table for the SEID, which isn't allowed in a subquery.
If you specify which column the subquery is looking for matches, that should solve your problem.

Match all values in a join view table

I have the following code where I create a view from two tables. I select few columns from each table. I tried to get the values from inventory.location_x and inventory.location_yfrom all the matches from both tables. However, it only selects one. It should return at least 3 pars of values.
Somebody can help me?
CREATE VIEW summary AS (
SELECT contexts, description, SKU, inventory.location_x, inventory.location_y
FROM inventory
LEFT JOIN product on inventory.epc_hex = product.epc having max(inventory.cycle)
);
Here there is a link to find an example of the two tables and the desired output:
https://www.dropbox.com/sh/k2imnxkqu412mp3/AABeIjgPUflPm8yx9nvr_uida?dl=0
In your query, you are using the "having" clause without specifying any aggregate functions in the select part of the sql.
Try removing the having clause and you will get more records. Try the following:
CREATE VIEW summary AS (
SELECT contexts, description, SKU, inventory.location_x, inventory.location_y
FROM inventory
LEFT JOIN product on inventory.epc_hex = product.epc
);
The following webpage gives examples on how to use the having clause:
http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html

How to select distinct rows from a table without a primary key

I need to show a Notification on user login if there is any unread messages. So if multiple users send (5 messages each) while the user is in offline these messages should be shown on login. Means have to show the last messages from each user.
I use joining to find records.
In this scenario Message from User is not a primary key.
This is my query
SELECT
UserMessageConversations.MessageFrom, UserMessageConversations.MessageFromUserName,
UserMessages.MessageTo, UserMessageConversations.IsGroupChat,
UserMessageConversations.IsLocationChat,
UserMessageConversations.Message, UserMessages.UserGroupID,UserMessages.LocationID
FROM
UserMessageConversations
LEFT OUTER JOIN
UserMessages ON UserMessageConversations.UserMessageID = UserMessages.UserMessageID
WHERE
UserMessageConversations.MessageTo = 743
AND UserMessageConversations.ReadFlag = 0
This is the output obtained from above query.
MessageFrom -582 appears twice. I need only one record of this user.
How is it possible
I'm not entirely sure I totally understand your question - but one approach would be to use a CTE (Common Table Expression).
With this CTE, you can partition your data by some criteria - i.e. your MessageFrom - and have SQL Server number all your rows starting at 1 for each of those partitions, ordered by some other criteria - this is the point that's entirely unclear from your question, whether you even care what the rows for each MessageFrom number are sorted on (do you have some kind of a MessageDate or something that you could order by?) ...
So try something like this:
;WITH PartitionedMessages AS
(
SELECT
umc.MessageFrom, umc.MessageFromUserName,
um.MessageTo, umc.IsGroupChat,
umc.IsLocationChat,
umc.Message, um.UserGroupID, um.LocationID ,
ROW_NUMBER() OVER(PARTITION BY umc.MessageFrom
ORDER BY MessageDate DESC) AS 'RowNum' <=== totally unclear yet
FROM
dbo.UserMessageConversations umc
LEFT OUTER JOIN
dbo.UserMessages um ON umc.UserMessageID = um.UserMessageID
WHERE
umc.MessageTo = 743
AND umc.ReadFlag = 0
)
SELECT
MessageFrom, MessageFromUserName, MessageTo,
IsGroupChat, IsLocationChat,
Message, UserGroupID, LocationID
FROM
PartitionedMessages
WHERE
RowNum = 1
Here, I am selecting only the "first" entry for each "partition" (i.e. for each MessageFrom) - ordered by a "imagined" MessageDate column so that the most recent (the newest) message would be selected.
Does that approach what you're looking for??
If you think of them as same rows, I assume you don't care about the message field.
In this case you can use the DISTINCT clause:
SELECT DISTINCT
UserMessageConversations.MessageFrom, UserMessageConversations.MessageFromUserName,
UserMessages.MessageTo, UserMessageConversations.IsGroupChat,
UserMessageConversations.IsLocationChat,
UserMessages.UserGroupID,UserMessages.LocationID
FROM
UserMessageConversations
LEFT OUTER JOIN
UserMessages ON UserMessageConversations.UserMessageID = UserMessages.UserMessageID
WHERE
UserMessageConversations.MessageTo = 743
AND UserMessageConversations.ReadFlag = 0
In general with distinct clause you have a row for every distinct group of row attributes.
If your requirement instead is to show a single field for all the messages (example: every message folded in a single message with a separator between them) you can use an aggregate function, but in SQL Server it seems is not that easy.