SQL query not working as expected | Duplicates and wrong results - mysql

HEyo folks.
alright, So i'm working on this little snippet of code here
SELECT
current.name,
current.status,
current.votes,
current.info,
current.votes_required,
current.sid as sid,
current.previous_required,
previous.sid as psid,
previous.status as status
FROM
systems as current
INNER JOIN
systems as previous
WHERE
(previous.status = 1 and current.previous_required = previous.status)
OR
(current.previous_required = 0)
'
Which Ideally I would like to show the results where either previous_required == 0 or where the previous_required's status == 1. However, for some reason, i keep either getting triple the results from my search (EG duplicates) or only a single result for the first one.
I'm pretty dang sure I know where i'm messing up (It's either the Inner Join or the OR statement). but I cannot seem to nail it down and fix the issue. Any input would be greatly appreciated.

You need to move the condition from where to join
SELECT
current.name,
current.status,
current.votes,
current.info,
current.votes_required,
current.sid as sid,
current.previous_required,
previous.sid as psid,
previous.status as status
FROM
systems as current
INNER JOIN
systems as previous
ON
(previous.status = 1 and current.previous_required = previous.status)
OR
(current.previous_required = 0)

It looks like you need to link the current records to their previous records somehow. Does your systems table have something like "previous_sid", which links a current record back to a previous one? If so, you need to use that in you inner join. Something like:
FROM
systems AS current
INNER JOIN
systems AS previous on current.previous_sid = previous.sid

Related

How to Join to a table where the result can sometimes lead with a - sign?

Hopefully i can explain this well enough. I have a bit of a unique issue where the customer system we use can change a ID in the database in the background based on the products status.
What this means is when i want to report old products we don't use anymore along side active products there ID differs between the two key tables depending on there status. This means Active products in the product table match that of the stock item table with both showing as 647107376 but when the product is no long active the StockItem table will present as 647107376 but the table that holds the product information the id presents as -647107376
This is proving problematic for me when i comes to joining the tables together to get the information needed. Originally i had my query set up like this:
SELECT
Company_0.CoaCompanyName
,SopProduct_0.SopStiStockItemCode AS hbpref
,SopProduct_0.SopStiCustomerStockCode AS itemref
,SopProduct_0.SopDescription AS ldesc
,StockMovement_0.StmOriginatingEntityID AS Goodsin
FROM
SBS.PUB.StockItem StockItem_0
LEFT JOIN SBS.PUB.SopProduct SopProduct_0 ON StockItem_0.StockItemID = SopProduct_0.StockItemID
LEFT JOIN SBS.PUB.Company Company_0 ON SopProduct_0.CompanyID = Company_0.CompanyID
LEFT JOIN SBS.PUB.StockMovement StockMovement_0 ON StockItem_0.StockItemID = StockMovement_0.StockItemID
WHERE
Company_0.CoaCompanyName = ?
AND StockMovement_0.MovementTypeID = '173355'
AND StockMovement_0.StmMovementDate >= ? AND StockMovement_0.StmMovementDate <= ?
AND StockMovement_0.StmQty <> 0
AND StockMovement_0.StockTypeID ='12049886'
Unfortunately though what this means is any of the old product will not show because there is no matching id due to the SopProduct table presenting the StockItemID with a leading -
So from this i thought best to use a case when statement with a nested concat and left in it to bring through the results but this doesn't appear to work either sample of the join below:
LEFT JOIN SBS.PUB.SopProduct SopProduct_0 ON (CASE WHEN LEFT(SopProduct_0.StockItemID,1) = "-" THEN CONCAT("-",StockItem_0.StockItemID) ELSE StockItem_0.StockItemID END) = SopProduct_0.StockItemID
Can anyone else think of a way around this issue? I am working with a Progress OpenEdge ODBC.
Numbers look like numbers. If they are, you can use abs():
ON StockItem_0.StockItemID = ABS(SopProduct_0.StockItemID)
Otherwise a relatively simple method is:
ON StockItem_0.StockItemID IN (SopProduct_0.StockItemID, CONCAT('-', SopProduct_0.StockItemID))
Note that non-equality conditions often slow down JOIN operations.
Using an or in the join should work:
LEFT JOIN SBS.PUB.SopProduct SopProduct_0
ON SopProduct_0.StockItemID = StockItem_0.StockItemID
OR
SopProduct_0.StockItemID = CONCAT("-", StockItem_0.StockItemID)
You might need to cast the result of the concat to a number (if the ids are stored as numbers).
Or you could use the abs function too (assuming the ids are numbers):
LEFT JOIN SBS.PUB.SopProduct SopProduct_0
ON SopProduct_0.StockItemID = abs(StockItem_0.StockItemID)

SQL Query seems not to affect the same number of rows, Adding a count statement

I have made a query that looks like this
Query 1.
SELECT zlec_status.nazwa AS Status,
piorytet.nazwa AS Priorytet,
Concat(koord.imie, ' ', koord.nazwisko) AS `Koordynator`,
Concat(zlec_adresy.miasto, ' - ', zlec_adresy.ulica, ' ',
zlec_adresy.oddzial)
AS `adres`,
zlec_z_dnia,zlec_id,
zlec_nr,
zlec_do,
zlec_ogran,
awizacje,
awizacja_na_dzien,
termin_zamkniecia,
tresc,
uwagi
FROM zlec
INNER JOIN koord
ON zlec.koord = koord.id
INNER JOIN zlec_adresy
ON zlec.zlec_addres = zlec_adresy.id
INNER JOIN piorytet
ON zlec.priorytet = piorytet.id
INNER JOIN zlec_status
ON zlec.status_zlecenia = zlec_status.id
And the following one which is a ordinary one
Query 2.
SELECT * FROM zlec;
The thing is the first one returns ( affects by executing ) 48 rows where the second query returns 103 rows. What could be the possible cause of this?
I will also show you my dumb of the sql in case you would like to make a run on your own http://pastebin.com/cMPAtxCU .
Subquestion - quite no point starting of with a new question for that because its also connected with the row count affect.
Besides I was wondering how can I get into the first query a count(*) to get the affected rows - it has to be done in sql I cannot use php code for that, probably it would be good to use a limit 1 for the count.
With INNER JOIN, if one of your other tables, koord, zlec_adresy, piorytet and zlec_status is missing a record corresponding to a record in zlec, that record in zlec will not be in the result set. If you want every record in zlec to appear, you have to use LEFT JOIN. Check out:
http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
For a pretty good explanation.
With your additional inner joins rows might be eliminated. Have you tried adding the inner joins to your "Select * FROM zlec;"?

SQL - Case in where clause

I got the following sql question that I that won´t work for me. I know that the last CASE row are wrong but I would like to use a CASE statement like that in my where clause.
Short description of my situation:
I got several companies that got there own material linked to them with "companyID". Each material might be linked to a row in pricelist_entries. If I search for one row in the pricelist_entries table that is linked to many material rows all rows will be returned but I just want to return the one that belongs to the current company (the company that performs the search).
Conclusion: If materialID NOT NULL THEN materials.company="current.companyID".
SELECT peID, peName, materialID
FROM pricelist_entries
INNER JOIN pricelist ON pricelist_entries.peParentID=pricelist.pID
LEFT JOIN materials ON pricelist_entries.peID=materials.pricelist_entries_id
WHERE peBrand = 'Kama' AND pricelist.pCurrent = 1
AND (peName LIKE '%gocamp de%' OR peArtnr LIKE '%gocamp de%')
AND pricelist.country=0 AND pricelist_entries.peDeleted=0
CASE materialID WHEN IS NOT NULL THEN materials.companyID=10 END
Please tell me if I need to describe my problem in a better way.
Thanks in advance!
Sounds like just moving the condition into the join would make it simpler;
SELECT peID, peName, materialID
FROM pricelist_entries
INNER JOIN pricelist
ON pricelist_entries.peParentID=pricelist.pID
LEFT JOIN materials
ON pricelist_entries.peID=materials.pricelist_entries_id
AND materials.companyID=10 -- << condition
WHERE peBrand = 'Kama' AND pricelist.pCurrent = 1
AND (peName LIKE '%gocamp de%' OR peArtnr LIKE '%gocamp de%')
AND pricelist.country=0 AND pricelist_entries.peDeleted=0
It will only left join in material rows that are linked to the correct company.
You can't use CASE in the where clause that I'm aware of, you need to use it in the SELECT portion, but it will have the same effect. Something like this should work:
SELECT CASE materialid WHEN IS NOT NULL THEN companyid END as thiscompanyid
This will give you a new column named thiscompanyid and you can query off of that to get what you need.

Improve terrible annotate() query

Here is the current query I have that orders an order_item by the most recent timestamp.
order_items.annotate(newest_note_time=Max('ordernotes__timestamp')).
order_by('newest_note_time')
It works. However, in viewing it in debug-toolbar it is giving me two brutal queries, that are all but identical. I have tried doing:
order_items = order_items.order_by('-ordernotes__timestamp')
But that results in an incorrect query that gives me duplicate results.
Is there a better way to do this query without jumping into raw SQL here?
Here is one of the queries (the second is basically identical, no idea why it generates a second...)
SELECT ••• FROM `order_orderitem`
INNER JOIN `order_orderitemstatus` ON (`order_orderitem`.`status_id` = `order_orderitemstatus`.`id`)
INNER JOIN `order_order` ON (`order_orderitem`.`order_id` = `order_order`.`id`)
INNER JOIN `title_title` ON (`order_orderitem`.`title_id` = `title_title`.`id`)
INNER JOIN `home_service` ON (`order_orderitem`.`service_id` = `home_service`.`id`)
LEFT OUTER JOIN `order_ordernotes` ON (`order_orderitem`.`id` = `order_ordernotes`.`order_item_id`)
WHERE NOT (`order_orderitemstatus`.`name` IN ('Complete', 'Live', 'Archived'))
GROUP BY
`order_orderitem`.`id`, `order_orderitem`.`order_id`, `order_orderitem`.`title_id`, `order_orderitem`.`service_id`,
`order_orderitem`.`metadata_locale_id`, `order_orderitem`.`purchase_order`, `order_orderitem`.`due_date`, `order_orderitem`.`feature`,
`order_orderitem`.`trailer`, `order_orderitem`.`artwork`, `order_orderitem`.`chaptering`, `order_orderitem`.`cc`,
`order_orderitem`.`metadata`, `order_orderitem`.`subtitles`, `order_orderitem`.`forced_narrative`, `order_orderitem`.`qc_note`,
`order_orderitem`.`audio`, `order_orderitem`.`dub_card`, `order_orderitem`.`live_url`, `order_orderitem`.`metadata_valid`,
`order_orderitem`.`status_id`, `order_orderitem`.`date_created`, `order_order`.`id`, `order_order`.`number`, `order_order`.`provider_id`,
`order_order`.`date_created`, `order_order`.`date_ordered`, `order_order`.`is_archived`, `title_title`.`id`, `title_title`.`film_id`,
`title_title`.`name`, `title_title`.`provider_id`, `title_title`.`original_locale_id`, `title_title`.`country_of_origin_id`,
`title_title`.`synopsis`, `title_title`.`production_company`, `title_title`.`copyright`, `title_title`.`run_time`,
`title_title`.`original_theatrical_release`, `title_title`.`color`, `title_title`.`film_type`, `title_title`.`no_cc_reason`,
`title_title`.`includes_hd`, `title_title`.`provider_identifier`, `title_title`.`episode_production_number`, `title_title`.`container_position`,
`title_title`.`season_id`, `home_service`.`id`, `home_service`.`name`, `home_service`.`notes`, `order_orderitemstatus`.`id`,
`order_orderitemstatus`.`name`, `order_orderitemstatus`.`department_id`, `order_orderitemstatus`.`is_finished`,
`order_orderitemstatus`.`ordering` ORDER BY NULL
.select_related(depth=1)
Add that to the end of your original query and see if any magic happens.
Another possible fix for awful queries that I have is to just cache the page. Add the following to the top of your file:
from django.views.decorators.cache import cache_page
and then just above your def... add:
#cache_page(60 * 5)
which is 60 seconds * 5, for 5 minutes. Change the time to whatever is appropriate for you.

SQL Query returns fake duplicate results?

I've been trying to write a few little plugins for personal use with WHMCS. Essentially what I'm trying to do here is grab a bunch of information about a certain order(s), and return it as an array in Perl.
The Perl bit I'm fine with, it's the MySQL query I've formed that's giving me stress..
I know it's big and messy, but what I have is:
SELECT tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting, tblproducts, tblorders, tblinvoices, tblservers
WHERE tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblservers.id = tblhosting.server
AND tblorders.id = tblhosting.orderid
AND tblinvoices.id = tblorders.invoiceid
AND tblinvoices.status = 'Paid'
I don't know if this /should/ work, but I assume I'm on the right track as it does return what I'm looking for, however it returns everything twice.
For example, I created a new account with the domain 'sunshineee.info', and then in PHPMyAdmin ran the above query.
id userid orderid packageid server domain username invoiceid gid ipaddress status
13 7 17 6 1 sunshineee.info sunshine 293 2 184.22.145.196 Paid
13 7 17 6 1 sunshineee.info sunshine 293 2 184.22.145.196 Paid
Could anyone give me a heads up on where I've gone wrong with this one.. Obvioiusly (maybe not obviously enough) I want this as only one row returned per match.. I've tried it with >1 domain in the database and it returned duplicates for each of the matches..
Any help would be much appreciated
:)
SELECT distinct tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting, tblproducts, tblorders, tblinvoices, tblservers
WHERE tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblservers.id = tblhosting.server
AND tblorders.id = tblhosting.orderid
AND tblinvoices.id = tblorders.invoiceid
AND tblinvoices.status = 'Paid'
Well, its near impossible without any table definitions, but you are doing a lot of joins there. You are starting with tblhosting.id and working your way 'up' from there. If any of the connected tables has a double entry, you'll get more hits
You could add a DISTINCT to your query, but that would not fix the underlying issue. It could be a problem with your data: do you have 2 invoices? Maybe you should select everything (SELECT * FROM) and check what is returned, maybe check your tables for double content.
Using DISTINCT is most of the time not a good choice: it means either your query or your data is incorrect (or you don't understand them thoroughly). It might get you the right result for now, but can get you in trouble later.
A guess about the reason this happens:
You do not connect the products table to the chain of id's. So you are basically adding a '2' to your result as far as I can see. You join on products, and the only thing that limits that table is that "gid" should be 2. So if you add a product with gid 2 you get another result. Either join it (maybe tblproduct.orderid = tblorders.id ? just guessing here) or just remove it, as it does nothing as far as I can see.
If you want to make your query a bit clearer, try not implicitly joining, but do it like this. So you can actually see what's happening
SELECT tblhosting.id, tblhosting.userid, tblhosting.orderid, tblhosting.packageid, tblhosting.server, tblhosting.domain, tblhosting.username, tblorders.invoiceid, tblproducts.gid, tblservers.ipaddress, tblinvoices.status
FROM tblhosting
JOIN tblproducts ON /*you're missing something here!*/
JOIN tblorders ON tblorders.id = tblhosting.orderid
JOIN tblinvoices ON tblinvoices.id = tblorders.invoiceid
JOIN tblservers ON tblservers.id = tblhosting.server
WHERE
tblorders.status = 'Pending'
AND tblproducts.gid = '2'
AND tblinvoices.status = 'Paid'
I don't see in your query JOIN to tblproducts, it seems to be a reason.