Searching in SQL per POST-Variable in joined Table - sql

I tried to solve this for some time and didn't come to a solution.
I read from a table filled with Mobile Devices and list them and give the user the possibilty to search the list per Movile Device Name and Username.
This is my Code:
SELECT
*
FROM
tbl_mobdev
LEFT JOIN
tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN
tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN
tbl_user ON tbl_user.id = mobdev_user
WHERE
CONCAT(tbl_marke.marke_name,' ',tbl_mobdev_type.mobdev_type_bezeichnung) LIKE '%".$_POST['marke_name']."%'
AND
CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
AND
mobdev_aktiv = '1'
ORDER BY
".$_GET['sort']." ".$_GET['sort2']."
Everything works as intended as long as tbl_mobdev.mobdev_user contains an User-ID, while table-rows that doesn't contain an User-ID are left out.
Any Solutions for this?
Edit: The solution of putting the "filter" in the LEFT JOIN for tbl_user doesn't work. An SQL like the following results in every row will be shown.
If the LIKE contains something only the not matching results will not be joined, the row doesn't get filtered.
SELECT mobdev_id, mobdev_type_bezeichnung, marke_name, tbl_user.name AS user_name, tbl_user.vorname AS user_vorname
FROM tbl_mobdev
LEFT JOIN tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN tbl_user ON tbl_user.id = mobdev_user
AND CONCAT( tbl_user.name, ' ', tbl_user.vorname ) LIKE '%%'
WHERE CONCAT( tbl_marke.marke_name, ' ', tbl_mobdev_type.mobdev_type_bezeichnung ) LIKE '%%'
AND mobdev_aktiv = '1'
ORDER BY marke_name ASC
LIMIT 0 , 30
Edit2: Setting the SQL to the following didn't changed the behavior.
SELECT mobdev_id, mobdev_type_bezeichnung, marke_name, tbl_user.name AS user_name, tbl_user.vorname AS user_vorname
FROM tbl_mobdev
LEFT JOIN tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN tbl_user ON tbl_user.id = mobdev_user
AND CONCAT( tbl_user.name, ' ', tbl_user.vorname ) LIKE '%G%'
WHERE CONCAT( tbl_marke.marke_name, ' ', COALESCE( tbl_mobdev_type.mobdev_type_bezeichnung, '' ) ) LIKE '%%'
AND mobdev_aktiv = '1'
ORDER BY marke_name ASC
LIMIT 0 , 30
Rows without matching $_POST['user'] still aren't filtered out.
I uploaded an example picture of the behavior with blurred out names, the left picute is a Select without any filter, right pictura is a Select with an actual Username (Blurred out, but its the exact same name as in mobdev_id '3'.
http://fs5.directupload.net/images/160404/tmdbyzws.png
As you can see, the other five rows are still there, just without the joins. I want them to be gone completely, because they are not matching the filter.
Edit3: Well, i made it now...sadly in a pretty dirty way using PHP. I just leave the code here, if someone comes up with a better solution for this please let me know.
$query = "
SELECT
mobdev_id,
mobdev_type_bezeichnung,
marke_name,
tbl_user.name AS user_name,
tbl_user.vorname AS user_vorname
FROM
tbl_mobdev
LEFT JOIN
tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN
tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN
tbl_user ON tbl_user.id = mobdev_user
WHERE
CONCAT(tbl_marke.marke_name,' ',tbl_mobdev_type.mobdev_type_bezeichnung) LIKE '%".$_POST['marke_name']."%'
";
if($_POST['user'])
{
$query .= "
AND
CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
";
}
$query .= "
AND
mobdev_aktiv = '1'
ORDER BY
".$_GET['sort']." ".$_GET['sort2']."
";

The problem is in the line
CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
Your LEFT JOIN on tbl_user will allow the main query to return independent of whether there's a value in that row. However, that particular WHERE clause line effectively overrides that; if tbl_user.name or tbl_user.vorname is null, the result will always be false.
If I need to do something like that, I'll often put the test into the ON clause of the JOIN statement. That way it filters the JOINed table where it exists, but doesn't cause problems where it doesn't.
Example query text that should ensure tbl_user gets filtered when required but not when nothing's specified -
SELECT
md.mobdev_id,
mdt.mobdev_type_bezeichnung,
m.marke_name,
u.name AS user_name,
u.vorname AS user_vorname
FROM
tbl_mobdev md
LEFT JOIN tbl_mobdev_type mdt
ON mdt.mobdev_type_id = md.mobdev_type
LEFT JOIN tbl_marke m
ON m.marke_id = mdt.mobdev_type_marke
LEFT JOIN tbl_user u
ON u.id = md.mobdev_user
AND CONCAT(u.name,' ',u.vorname) LIKE '%".$_POST['user']."%'
WHERE
CONCAT(m.marke_name,' ',COALESCE(mdt.mobdev_type_bezeichnung,''))
LIKE '%".$_POST['m.marke_name']."%'
AND md.mobdev_aktiv = '1'
ORDER BY
".$_GET['sort']." ".$_GET['sort2']."
Two asides -
As Rene M. says above, this code is an SQL Injection vulnerability. It might be safe due to its current application but I still wouldn't roll it out; the chances of someone taking this application or code fragment and later moving it online without doing a full audit is not insignificant, and you've then got a problem. Practice doing it right - don't leave SQL Injection vulnerabilities in code.
Please alias your table names! The query having every table name written out in full for each field reference makes it very bulky.

Related

Blob in query with joins to create a view

I am struggling with a blob-type-column to join it to a view with multiple joins in it.
The blob-type is the MAINAPL.GRAPHICS.GRAF and highlighted below in my query.
I get following Error from my Oracle Database:
ORA-00932 and it leads to exactly that column.
Is there a possibility to join the blob-type anyways? I need it as a normal column in this complex view.
My main query is this:
CREATE OR REPLACE FORCE VIEW SECAPL.VIEW_DATAFEED2 AS
SELECT
MIN(CASE WHEN MAINAPL.ARTCOPY.SPRID = 'EN' AND MAINAPL.ARTCOPY.ARTCOPYNUM = 1 THEN MAINAPL.ARTCOPY.ART-COPY-1 ELSE NULL END ) COPY1-EN,
MAX(CASE WHEN MAINAPL.CONT.TONGID = 'EN' AND MAINAPL.CLASS1.CLASSTFRLE1 = '1' THEN MAINAPL.CONT.COPY-ONLY END) AS TYPE-EN,
MAINAPL.ARTICLEGRAPHICS.GRAPHID,
**MAINAPL.GRAPHICS.GRAF**,
MAINAPL.GRAPHICS.GRAFFORMAT
FROM
MAINAPL.ARTICLE
LEFT JOIN MAINAPL.ARTCOPY ON MAINAPL.ARTICLE.ARTID = MAINAPL.ARTCOPY.ARTID
INNER JOIN MAINAPL.ARTICLEGRAPHICS ON MAINAPL.ARTICLE.ARTID = MAINAPL.ARTICLEGRAPHICS.ARTID
INNER JOIN MAINAPL.GRAPHICS ON MAINAPL.GRAPHICS.GRAPHICSID = MAINAPL.ARTICLEGRAPHICS.GRAPHICSID
GROUP BY MAINAPL.ARTICLE.ARTID,
MAINAPL.ARTICLEGRAPHICS.GRAPHID,
**MAINAPL.GRAPHICS.GRAF**,
MAINAPL.GRAPHICS.GRAFFORMAT
I think the problem is that you are trying to group by the blob column. You can work around that easily. Use two selects. The first select is basically what you have but without the blob (and possibly without other columns that can be fetched later). You still get the graphid. The second outer select joins the inner select with the mainapl.grapics table on graphid and returns everything including the blob. Hope that made sense?
EDIT:
CREATE OR REPLACE FORCE VIEW SECAPL.VIEW_DATAFEED2 AS
select sub.copy1-en, sub.type-en, sub.graphicsid,
graph.graf, graph.grafformat
from (SELECT
MIN(CASE WHEN MAINAPL.ARTCOPY.SPRID = 'EN' AND MAINAPL.ARTCOPY.ARTCOPYNUM = 1 THEN MAINAPL.ARTCOPY.ART-COPY-1 ELSE NULL END ) COPY1-EN,
MAX(CASE WHEN MAINAPL.CONT.TONGID = 'EN' AND MAINAPL.CLASS1.CLASSTFRLE1 = '1' THEN MAINAPL.CONT.COPY-ONLY END) AS TYPE-EN,
MAINAPL.ARTICLEGRAPHICS.GRAPHICSID
FROM MAINAPL.ARTICLE
LEFT JOIN MAINAPL.ARTCOPY ON MAINAPL.ARTICLE.ARTID = MAINAPL.ARTCOPY.ARTID
INNER JOIN MAINAPL.ARTICLEGRAPHICS ON MAINAPL.ARTICLE.ARTID = MAINAPL.ARTICLEGRAPHICS.ARTID
INNER JOIN MAINAPL.GRAPHICS ON MAINAPL.GRAPHICS.GRAPHICSID = MAINAPL.ARTICLEGRAPHICS.GRAPHICSID
GROUP BY MAINAPL.ARTICLEGRAPHICS.GRAPHICSID
) sub
join MAINAPL.GRAPHICS ON MAINAPL.GRAPHICS.GRAPHICSID = sub.GRAPHICSID
This may not work, but should illustrate the point. I rewrote the inner SQL a bit as you seemed to group by too much, I'm not sure if that was good or bad. I also corrected a possible mistake with the graphicsid column name.
Anyway, the point should be clear - find the records you need with an inner SQL that includes the id you need in order to fetch the blob in the outer SQL.

Select a field called "return" in postgreSQL

I'm having a problem with a query in postgres, the table cgporders_items has a field called return, I cannot get actual result of that field with this query, it returns me al ceros.
SELECT "Cgporder".id AS "Cgporder__id"
,"Sale".preorder_number AS "Sale__preorder_number"
,"Contact".id AS "Contact__id"
,"Contact".NAME AS "Contact__name"
,"Ptype".NAME AS "Ptype__name"
,(
SELECT code
FROM products
WHERE id = "CgporderItem".parent_id
) AS "Product__parent_code"
,"Product".id AS "Product__id"
,"Product".code AS "Product__code"
,"Product".NAME AS "Product__name"
,"CgporderItem".quantity AS "CgporderItem__quantity"
,"CgporderItem".return AS "CgporderItem__return"
,"CgporderItem".cep_id AS "CgporderItem__cep"
FROM cgporders AS "Cgporder"
INNER JOIN contacts AS "Contact" ON ("Contact".id = "Cgporder".contact_id)
INNER JOIN cgporders_items AS "CgporderItem" ON ("Cgporder".id = "CgporderItem".cgporder_id)
INNER JOIN products AS "Product" ON ("Product".id = "CgporderItem".product_id)
INNER JOIN ptypes AS "Ptype" ON ("Ptype".id = "Product".ptype_id)
LEFT JOIN cgporders_sales AS "CgporderSale" ON ("Cgporder".id = "CgporderSale".cgporder_id)
LEFT JOIN sales AS "Sale" ON ("Sale".id = "CgporderSale".sale_id)
WHERE "CgporderItem".parent_id != 0
AND "Cgporder"."issue_date" >= '2015-11-27'
AND "Cgporder"."issue_date" <= '2015-11-27'
AND "Cgporder"."status" = 'confirmed'
ORDER BY "Ptype".NAME
,"Product"."code";
There are actually a lots of rows that matches the select condition, but it return cero on "CgporderItem".return AS "CgporderItem__return"
If I make a simple query like select "return" from cgporders_items it works. But in this query it does not work.
Can you help me please?
"return" is a reserved word in SQL, but not in Postgres. See the list here. The following code works find in Postgres (SQL Fiddle is here):
create table dum (return int);
select dum.return from dum;
Your problem is something else. If I had to guess, the where clause is too restrictive (the condition on dates is a bit suspect).

Best way to optimize this SQL Query

I have to optimize this query and I am really in a hurry here. The following query searches by client. The input value RIF.keyvaluechar
LIKE 'V%10553790 ' is because in some old registers in the database some IDs when missing characters it used to be V0012345678 but it should have been V12345678 as that's the maximum amount of characters the ID can have. I know 12345678 should have been numeric and the V a char and then compare, but that's another issue.
Anyway, the query is this one:
SELECT DISTINCT idata.itemnum AS [ID],
LTRIM(RTRIM(ISNULL(CONTRATO.keyvaluechar,'N/A'))) AS [Contrato],
idata.datestored AS [Fecha],
NUMERO.keyvaluesmall AS [Numero],
TIPO.keyvaluechar AS [Tipo],
LTRIM(RTRIM(ISNULL(LC.lifecyclename,'N/A'))) AS [Flujo],
LTRIM(RTRIM(ISNULL(LC.lcnum,-1))) AS [FlujoID],
LTRIM(RTRIM(ISNULL(LCS.statename,'N/A'))) AS [Cola],
LTRIM(RTRIM(ISNULL(LCS.statenum,-1))) AS [ColaID],
CASE
WHEN PC.NombreProceso IN('PTD','PV2','PV3') THEN 1
ELSE 0
END AS [Portada]
FROM OnBase.hsi.itemdata idata WITH (NOLOCK)
INNER JOIN OnBase.hsi.keyitem109 TIPO WITH (NOLOCK) ON TIPO.itemnum = idata.itemnum
INNER JOIN OnBase.hsi.keyitem113 NUMERO WITH (NOLOCK) ON NUMERO.itemnum = idata.itemnum
LEFT JOIN OnBase.hsi.keyitem132 CONTRATO WITH (NOLOCK) ON CONTRATO.itemnum = idata.itemnum
LEFT JOIN OnBase.hsi.keyitem114 CLIENTE WITH (NOLOCK) ON CLIENTE.itemnum = idata.itemnum
LEFT JOIN OnBase.hsi.keyitem111 RIF WITH (NOLOCK) ON RIF.itemnum = idata.itemnum
INNER JOIN OnBase.hsi.doctype DOC WITH (NOLOCK) ON DOC.itemtypenum = idata.itemtypenum
INNER JOIN BD_WorkFlow.dbo.BBVA_ProcesosConfig PC WITH (NOLOCK) ON PC.ID_Documento = idata.itemtypenum
LEFT JOIN Onbase.hsi.itemlc ILC WITH (NOLOCK) ON ILC.itemnum = idata.itemnum
LEFT JOIN Onbase.hsi.lcstate LCS WITH (NOLOCK) ON LCS.statenum = ILC.statenum
LEFT JOIN Onbase.hsi.lifecycle LC WITH (NOLOCK) ON LC.lcnum = ILC.lcnum
WHERE PC.NombreProceso <> 'XXX' AND
PC.NombreProceso NOT IN('PTD','PV2','PV3') AND
TIPO.keyvaluechar = 'CCD' AND
RIF.keyvaluechar LIKE 'V%10553790 '
As you can see it is this way so it finds V0012345678 or V12345678 but this is not the right way or I feel it is the best optimization, although I am no expert in databases.
Anyways, I've though about something like this instead of last line
AND LEFT ('RIF.Keyvaluechar, 1) ="V"
AND SUBSTRING (RIF.Keyvaluechar, 2, LEN(RIF.Keyvaluechar)) = "12345678"
What do you guys think? Is there any other better way to improve upon this?
First, your query has a logic problem. You have this:
LEFT JOIN OnBase.hsi.keyitem111 RIF WITH(NOLOCK) ON RIF.itemnum = idata.itemnum
and then this in your where clause:
AND RIF.keyvaluechar LIKE 'V%10553790 '
Putting that filter in your where clause effectively changes your left join to an inner join. To fix this, move the filter to the join.
In terms of optimizing it, I assume that means to make it run faster. What you were thinking about will probably slow things down because you are filtering on function results instead of fields. A better approach, no matter how much of a hurry you are in, is to look at the indexes in your database and try to filter on those. In fact, it might be appropriate to add new ones.
Is the Keyvaluechar always a number from the second character onwards and you want to treat it as a number (=remove leading zeros). You could try to add a persisted column convert(int, SUBSTRING (Keyvaluechar, 2, 10)) to the table, then index that, and use it as a search criteria. At least I would assume that should help a lot.
In addition to that, looking at statistics IO output might be a good idea too, to see what table is actually responsible for the biggest I/O amounts.
Just a note, I hope you also know the problems using NOLOCK can cause you.

SQL Table Joins - What am I doing wrong?

I'm in a intro to database class, and one of my queries is seriously giving me trouble.
The assignment says to: Write a query to display the Passenger name, Seat No and Destination. Display this in one column title Travellers_info. This column should display data in the following format “ Mary Ann Jenkins is assigned to Seat 15 on the way to Bellmead”
This is the relationship view: http://prntscr.com/1jsoay
Can somebody please help me out, I'm not sure where I've gone wrong.
SELECT passenger.name + 'is assigned to Seat'
+ seat_info.seat_no + 'on the way to'
+ departure_info.destination AS Travellers_info
FROM passenger, seat_info, departure_info, seat_passenger, manages
WHERE passenger.Pass_id=seat_passenger.pass_id
AND seat_passenger.Seat_id=seat_info.Seat_id
AND seat_info.seat_id=manages.Seat_id
AND manages.Dept_id=departure_info.dept_id
You cant "add" text values using the "+" operator.
Without knowing which database you are using, the solution is probably either using CONCAT():
SELECT concat(passenger.name, 'is assigned to Seat', seat_info.seat_no,
'on the way to', departure_info.destination) AS Travellers_info
FROM passenger, seat_info, departure_info, seat_passenger, manages
WHERE passenger.Pass_id=seat_passenger.pass_id
AND seat_passenger.Seat_id=seat_info.Seat_id
AND seat_info.seat_id=manages.Seat_id
AND manages.Dept_id=departure_info.dept_id
or using the || operator:
SELECT passenger.name || 'is assigned to Seat'
|| seat_info.seat_no || 'on the way to'
|| departure_info.destination AS Travellers_info
FROM passenger, seat_info, departure_info, seat_passenger, manages
WHERE passenger.Pass_id=seat_passenger.pass_id
AND seat_passenger.Seat_id=seat_info.Seat_id
AND seat_info.seat_id=manages.Seat_id
AND manages.Dept_id=departure_info.dept_id
Have you tried double-checking all your tables (especially the joining tables like "manages" and "seat_passenger") to make sure you have valid data in them that would appropriately join up?
Also, make sure you have spaces in your text.
e.g.: 'is assigned to Seat' should be ' is assigned to Seat '
If you are getting no data it must be in your join criteria. Reduce your select to just a column from your first table and then join each table one at a time and see when it is that you stop getting data back
First off, you're going to want to put a space before is so that it won't look mushed together.
passenger.name + ' is assigned to seat'
Typically, you use subqueries and left outer joins for something of this sort. You do not just want to pull out all the data at once from all those tables. Let me see if I can explain this with some SQL. If you don't understand my example, go here: http://thenewboston.org/watch.php?cat=49&number=20 and watch 20-23 and you'll for sure understand it.
SELECT c.name + ' is assigned to Seat ' + c.seat_no + ' on the way to ' + departure_info.destination as Travellers_info
FROM departure_info
LEFT OUTER JOIN (SELECT b.Pass_id, b.name, b.seat_id, b.seat_no
FROM seat_no
LEFT OUTER JOIN (SELECT a.Pass_id, a.name, seat_passenger.seat_id
FROM seat_passenger
LEFT OUTER JOIN (SELECT passenger.name, passenger.Pass_id
from passenger
LEFT OUTER JOIN
ON passenger.Pass_id = seat_passenger.Pass_id) a seat_info
ON a.seat_id = seat_info.seat_id) b manages
ON b.seat_id = manages.seat_id) c departure_info
ON c.Dept_id = departure_info.Dept_id
I do not believe this will yield you the correct answer. But, it will get you working towards the right answer. I think I made a mistake after the third subquery. Remember that SQL evaluates things from the inside, then goes out. Just keep that in mind. Hope this helps.
try this in MSSQL or MSAccess.
for query optimization use ON (Join)
SELECT passenger.name + 'is assigned to Seat '
+ ltrim(rtrim(convert(char,seat_info.seat_no))) + ' on the way to'
+ departure_info.destination AS Travellers_info
FROM passenger p
join seat_passenger sp on sp.pass_id = p.Pass_id
join seat_info s on s.Seat_id = sp.Seat_id
join manages m on m.Seat_id = s.Seat_id
join departure_info d on d.dept_id = m.dept_id
concat in 2008R2 is not a valid function so you need to convert a number data to character.
while in 2012 it is available.

Conditional SQL JOIN based on column contents

I have a table, event_sessions that contains a session_submitted_by ID and a session_submitter_type. I need to JOIN session_submitted_by to one of two different tables, based on the contents of session_submitter_type.
I can't just do an LEFT OUTER JOIN on both tables and choose whichever returns content because both tables may contain an ID match.
Here is my improper SQL/pseudo-code that shows what I'm trying to accomplish:
SELECT es.event_session_id,
subu.first_name + ' ' + subu.last_name as submitted_by_name,
subu.email as submitter_email
FROM event_session es
CASE WHEN es.session_submitter_type = 'speaker'
THEN
LEFT OUTER JOIN speakers subu
ON subu.speaker_id = es.session_submitted_by
ELSE
LEFT OUTER JOIN users subu
ON subu.user_id = es.session_submitted_by
END
Any suggestions how to code this without having to resort to a UNION?
Interesting question. I would do it like so:
SELECT es.event_session_id,
coalesce(spk.first_name, usr.first_name)
+ ' ' + coalesce(spk.last_name, usr.last_name) as submitted_by_name,
coalesce(spk.email, usr.email) as submitter_email
FROM event_session es
LEFT OUTER JOIN speakers spk
ON es.session_submitter_type = 'speaker' and spk.speaker_id = es.session_submitted_by
LEFT OUTER JOIN users usr
ON es.session_submitter_type <> 'speaker' and usr.user_id = es.session_submitted_by
You basically use two left outer joins, and one only one of them is ever going to be active for any given row of event_session.
Note: If session_submitter_type is nullable then you need to add a NULL check in the second join to maintain the semantics of your pseudo code.