add view state from other table to select result - mysql

I have two tables reports & viewedids
the first one stores data and the second one stores user IDs that viewed data:
viewedids:
+-------+------+
| uid | rid |
+-------+------+
| 2 | 5 |
+-------+------+
each (uid,rid) means the uid has viewd rid
I want to select * from reports table and add view state (0 or 1) for current user to it. (A JOIN statement)
How can I do this?

You can use a LEFT JOIN:
SELECT reports.*, viewedids.uid IS NOT NULL as view_state
FROM
reports LEFT JOIN viewedids
ON reports.id = viewedids.rid
AND viewedids.uid = #current_user
This will return all reports, and will try to join reports table with viewedids ON reports.id = viewedids.rid AND viewedids.uid = #current_user. If the join succedes, viewedids.uid will be not null, and viewedids.uid IS NOT NULL will be evaluated to 1. It will be evaluated 0 otherwise.
Please see fiddle here.

Related

SQL-Query - Join multiple tables with additional dynamic column

I need help for building a SQL-statement. The Database-Schema looks like this:
I did prepare the following SQL-Fiddle, which contains sample-data:
http://sqlfiddle.com/#!9/831bab/1
As for the sample-data in the SQL-Fiddle, I want to query for the computername "Client02" and want to get the following result:
Wanted result 1
PrinterName | PrintServer | PrinterActive | ComputerActive | isDefaultPrinter
PRT01_Zentral | DC01 | True | True | False
PRT02_BH | DC01 | True | True | True
As for the sample-data in the SQL-Fiddle, I want to query for the computername "Client01" and want to get the following result:
Wanted result 2
PrinterName | PrintServer | PrinterActive | ComputerActive | isDefaultPrinter
PRT01_Zentral | DC01 | True | True | True
As you see, I need to join all the tables and add something like a helper-column, which contains information about the default-printer. (True/False)
I started to build up the query, but I don't know how to proceed ...
SELECT printers.PrinterName, printers.PrintServer, printers.PrinterActive
FROM computermapping
LEFT JOIN computers ON computermapping.ComputerID = computers.ComputerID
LEFT JOIN printers ON computermapping.PrinterID = printers.PrinterID
LEFT JOIN computerdefaultprinter ON computers.ComputerID = computerdefaultprinter.ComputerID
WHERE computers.ComputerName = "Client02"
I think my request contains all the information, which is needed. The SQL-Fiddle has sample-data to easily reproduce it. The WantedResults should show the target clearly.
EDIT:
DBMS: MySQL
You could use the following query to get the result that you need
SELECT printers.PrinterName,
printers.PrintServer,
printers.PrinterActive,
computers.ComputerActive,
CASE
WHEN computerdefaultprinter.PrinterID IS NULL THEN "false"
ELSE "true"
END AS isDefaultPrinter
FROM computermapping
LEFT JOIN computers
ON computermapping.ComputerID = computers.ComputerID
LEFT JOIN printers
ON computermapping.PrinterID = printers.PrinterID
LEFT JOIN computerdefaultprinter
ON computers.ComputerID = computerdefaultprinter.ComputerID
AND printers.PrinterID = computerdefaultprinter.PrinterID
WHERE computers.ComputerName = "Client02"
I've only added CASE...WHEN statement and modified join condition of your original query.
But I think instead of using table computerdefaultprinter, why don't you add column IsDefaultPrinter to table computermapping.
CREATE TABLE `computermapping` (
`ComputerMappingID` int(11) NOT NULL,
`PrinterID` int(11) NOT NULL,
`ComputerID` int(11) NOT NULL,
`IsDefaultPrinter` int(1) NOT NULL DEFAULT 0,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
What I did was add a if statement in your query, comparing the inner result (that can only be 1) to your outter select. There is a better way of achieving this probably but this answer reflects the queryresult's that you've asked.
Edit: I'm assuming only a default printer per computer.
Are you sure the wanted result 2 is correct? Because in the database, records are saying computer1 has 1 printerid (1) that is the default and that computer2 has 2 printers (1 and 2) and 2 is default.
SELECT printers.PrinterName,
printers.PrintServer,
printers.PrinterActive,
IF (
(SELECT computerdefaultprinter.printerid
FROM computerdefaultprinter
WHERE computerdefaultprinter.computerid = computermapping.computerid) = computermapping.printerid,
'true',
'false') AS isDefaultPrinter
FROM computermapping
LEFT JOIN computers ON computermapping.ComputerID = computers.ComputerID
LEFT JOIN printers ON computermapping.PrinterID = printers.PrinterID
LEFT JOIN computerdefaultprinter ON computers.ComputerID = computerdefaultprinter.ComputerID
WHERE computers.ComputerName = "Client02"

MySQL left join with default values

I have a couple of tables, one with source data which I'll call SourceData and another which defines overridden values for a given user if they exist called OverriddenSourceData.
The basic table format looks something this like:
SourceData
| source_id | payload |
--------------------------------
| 1 | 'some json' |
| 2 | 'some more json' |
--------------------------------
OverriddenSourceData
| id | source_id | user_id | overrides
| 1 | 2 | 4 | 'a change' |
------------------------------------------
For a given user, I'd like to return all the Source data rows with the overrides column included. If the user has overridden the source then the column is populated, else it is null.
I started by executing a left join and then including a condition for checking the user like so:
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
WHERE user_id = 4
but then source rows that weren't overridden wouldn't be included ( it was acting like an inner join) (e.g source id 1)
I then relaxed the query and used a strict left join on source_id.
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
# WHERE user_id = 4
This can return more data than I need though (e.g other users who have overridden the same source data) and then I have to filter programatically.
It seems like I should be able to craft a query that does this all the DB level and gives me what I need. Any help?
You should add your condition on LEFT JOIN clause, if you use WHERE, mysql will do it with INNER JOIN, so try this;)
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
AND B.user_id = 4

Find records which matches certain constrain based on NOT EXIST

There is a query which select some data. I'm creating a suppression table which would ignore the rows containing certain data.
suppression: occasion_id | days_before
reminder: id | days_before | occasion_id
I'm making use of NOT EXIST to ignore selection of certain reminders.
The query is
SELECT id
from Reminder AS r
WHERE NOT EXIST (SELECT 1
FROM Suppression s
WHERE s.occasion_id = r.occasion_id
AND s.days_before = r.days_before)
Reminder:1) 101| 1 |18
2) 102| 7| 18
Suppresion: 18 | 1
The 1st reminder should be ignored and the 2nd one should be included.
As an example, if suppression table contain occasion_id - 18 and days_before- 1 the select should ignore reminder containing those data.
The sub query returns '1' in the case of 2nd reminder also. Why does it happens even if the statement after WHERE clause yields no result?
You are joining by the wrong condition.
Change the joining condition from s.id = r.id to s.occasion_id = r.occasion_id
SELECT r.*
FROM Reminder AS r
inner JOIN Supression s ON s.occasion_id = r.occasion_id
AND s.days_before <> r.days_before
Fiddle
Your query also works.. Just change the joining condition
SELECT id
from Reminder AS r
WHERE NOT EXISTs (SELECT 1
FROM Supression s
WHERE s.occasion_id = r.occasion_id
AND s.days_before = r.days_before)
I believe what you're trying to accomplish could be more easily done with a simple LEFT JOIN and an IS NULL in your where clause:
SELECT r.id
FROM Reminder AS r
LEFT JOIN Supression s ON s.id = r.id AND s.days_before = r.days_before
WHERE s.id IS NULL

MYSQL - Select only if row in LEFT JOIN is not present

I have 2 simple mysql tables. The first 1 called mail and has 2 rows:
sender | receiver
Marley | Bob
Saget | Bob
The second one called block and has 1 row:
blocker | blocked
Bob | Marley
I want to select sender(s) from the first table who sent Bob emails but aren't blocked in the block table. So the results should be:
sender
saget
I tried the following query but it's not returning results:
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = 'Bob')
WHERE (block.blocked <> mail.sender)
The left join will produce null rows for the mismatches.
It's those null rows that you need to filter on.
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = 'Bob')
WHERE block.blocker IS NULL
It's kind of strangle to be joining on a fixed value however, a more common join (given your tables) would be:
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = mail.receiver
and block.blocked = mail.sender)<<-- these should match
WHERE block.blocker IS NULL <<-- select only mismatches
AND mail.receiver like 'bob';
Try this:
SELECT sender
FROM mail m
WHERE NOT EXISTS (SELECT 1 FROM block
WHERE blocker = m.receiver
AND blocked = m.sender)

Using DISTINCT inside JOIN is creating trouble [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How can I modify this query with two Inner Joins so that it stops giving duplicate results?
I'm having trouble getting my query to work.
SELECT itpitems.identifier, itpitems.name, itpitems.subtitle, itpitems.description, itpitems.itemimg, itpitems.mainprice, itpitems.upc, itpitems.isbn, itpitems.weight, itpitems.pages, itpitems.publisher, itpitems.medium_abbr, itpitems.medium_desc, itpitems.series_abbr, itpitems.series_desc, itpitems.voicing_desc, itpitems.pianolevel_desc, itpitems.bandgrade_desc, itpitems.category_code, itprank.overall_ranking, itpitnam.name AS artist, itpitnam.type_code FROM itpitems
INNER JOIN itprank ON (itprank.item_number = itpitems.identifier)
INNER JOIN (SELECT DISTINCT type_code FROM itpitnam) itpitnam ON (itprank.item_number = itpitnam.item_number)
WHERE mainprice > 1
LIMIT 3
I keep getting Unknown column 'itpitnam.name' in 'field list'.
However, if I change DISTINCT type_code to *, I do not get that error, but I do not get the results I want either.
This is a big result table so I am making a dummy example...
With *, I get something like:
+-----------+---------+----------+
| identifier| name | type_code|
+-----------+---------+----------+
| 2 | Joe | A |
| 2 | Amy | R |
| 7 | Mike | B |
+-----------+------------+-------+
The problem here is that I have two instances of identifier = 2 because the type_code is different. I have tried GROUP BY at the outside end of the query, but it is sifting through so many records it creates too much strain on the server, so I'm trying to find an alternative way of getting the results I need.
What I want to achieve (using the same dummy output) would look something like this:
+-----------+---------+----------+
| identifier| name | type_code|
+-----------+---------+----------+
| 2 | Joe | A |
| 7 | Mike | B |
| 8 | Sam | R |
+-----------+------------+-------+
It should skip over the duplicate identifier regardless if type_code is different.
Can someone help me modify this query to get the results as simulated in the above chart?
One approach is to use an inline view, like the query you already have. But instead of using DISTINCT, you would use a GROUP BY to eliminate duplicates. The simplest inline view to satisfy your requirements would be:
( SELECT n.item_number, n.name, n.type_code
FROM itpitnam n
GROUP BY n.item_number
) itpitnam
Although its not deterministic as to which row from itpitnam the values for name and type_code are retrieved from. A more elaborate inline view can make this more specific.
Another common approach to this type of problem is to use a correlated subquery in the SELECT list. For returning a small set of rows, this can perform reasonably well. But for returning large sets, there are more efficient approaches.
SELECT i.identifier
, i.name
, i.subtitle
, i.description
, i.itemimg
, i.mainprice
, i.upc
, i.isbn
, i.weight
, i.pages
, i.publisher
, i.medium_abbr
, i.medium_desc
, i.series_abbr
, i.series_desc
, i.voicing_desc
, i.pianolevel_desc
, i.bandgrade_desc
, i.category_code
, r.overall_ranking
, ( SELECT n1.name
FROM itpitnam n1
WHERE n1.item_number = r.item_number
ORDER BY n1.type_code, n1.name
LIMIT 1
) AS artist
, ( SELECT n2.type_code
FROM itpitnam n2
WHERE n2.item_number = r.item_number
ORDER BY n2.type_code, n2.name
LIMIT 1
) AS type_code
FROM itpitems i
JOIN itprank r
ON r.item_number = i.identifier
WHERE mainprice > 1
LIMIT 3
That query will return the specified resultset, with one significant difference. The original query shows an INNER JOIN to the itpitnam table. That means that a row will be returned ONLY of there is a matching row in the itpitnam table. The query above, however, emulates an OUTER JOIN, the query will return a row when there is no matching row found in itpitnam.
UPDATE
For best performance of those correlated subqueries, you'll want an appropriate index available,
... ON itpitnam (item_number, type_code, name)
That index is most appropriate because it's a "covering index", the query can be satisfied entirely from the index without referencing data pages in the underlying table, and there's equality predicate on the leading column, and an ORDER BY on the next two columns, so that will a avoid a "sort" operation.
--
If you have a guarantee that either the type_code or name column in the itpitnam table is NOT NULL, you can add a predicate to eliminate the rows that are "missing" a matching row, e.g.
HAVING artist IS NOT NULL
(Adding that will likely have an impact on performance.) Absent that kind of guarantee, you'd need to add an INNER JOIN or a predicate that tests for the existence of a matching row, to get an INNER JOIN behavior.
SELECT a.*
b.overall_ranking,
c.name AS artist,
c.type_code
FROM itpitems a
INNER JOIN itprank b
ON b.item_number = a.identifier
INNER JOIN itpitnam c
ON b.item_number = c.item_number
INNER JOIN
(
SELECT item_number, MAX(type_code) code
FROM itpitnam
GROUP BY item_number
) d ON c.item_number = d.item_number AND
c.type_code = d.code
WHERE mainprice > 1
LIMIT 3
Follow-up question: can you please post the table schema and how are the tables related with each other? So I will know what are the columns to be linked.