SQL QUERY not giving required result when documents =0 - sql-server-2008

I have one record with no documents (i.e., Documents= 0). When I am executing the following query, it returns zero rows, but it should return my one row since I have one record with no documents.
How can I modify this so it will return the record with no documents?
SELECT * FROM (SELECT ROW_NUMBER() OVER( Order By ParentID  ) AS RowNumber_ps,
UPPER(HostApplicationLocalData.ParentID) asParentID,
COUNT(Document.ID) as Documents from HostApplicationLocalData
inner join Document onHostApplicationLocalData.ID=Document.HostApplicationLocalData_ID
WHERE HostApplicationLocalData.TransactionType_ID = 1
AND Document.Removed=0
AND HostApplicationLocalData.Company_ID = 9000
AND ( SharePointURI is not null or ((SharePointURI isnull and Content_ID is not null )
or ((HostApplicationLocalData_ID is not null andHostApplicationLocalData_ID != 0 and Content_ID isnull))))
group by HostApplicationLocalData.ParentID )q
where Documents > 0
And
Documents = '0'

Looking over your code, it seems most likely that the INNER JOIN (along with the filter Document.Removed=0) is excluding any record without an entry in the Document table, which would naturally mean that no values would show up with a COUNT(Document.ID = 0.
Try this version of the query, which converts the JOIN type to a LEFT OUTER, and adds a condition allowing the Document.Removed filter to pass through records where the Document record would return NULL.
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER( Order By ParentID ) AS RowNumber_ps,
UPPER(HostApplicationLocalData.ParentID) asParentID,
COUNT(Document.ID) as Documents
FROM
HostApplicationLocalData
LEFT OUTER JOIN
Document ON
HostApplicationLocalData.ID=Document.HostApplicationLocalData_ID
WHERE
HostApplicationLocalData.TransactionType_ID = 1
AND (Document.Removed=0 OR Document.HostApplicationLocalData_ID IS NULL)
AND HostApplicationLocalData.Company_ID = 9000
AND
(SharePointURI is not null or
((SharePointURI isnull and Content_ID is not null ) or
((HostApplicationLocalData_ID is not null andHostApplicationLocalData_ID != 0 and Content_ID isnull))
)
)
GROUP BY HostApplicationLocalData.ParentID
)q
WHERE Documents = 0

Related

Order query only by a specific field if all values from result set are NULL

I am building a custom document versioning system in my website.
For this, I have 2 tables "vrm_document" and "vrm_document_active_document".
The first table contains all documents on my website, while the second table tells me which document is the active version for which user, so the second table contains the vrm_document_id field and a user_id field. Active documents are indicated by the parent_vrm_document_id being not NULL.
I have 2 kinds of documents:
Default documents
Documents uploaded by the users themselves
Each document that is uploaded contains versions, and min. 1 and max. 1 version has to be active at all times for the user. Thus the second table vrm_document_active_document will always contain the vrm_document_id combined with the user_id for the documents uploaded by the user himself.
However, the problem resides in the fact that default documents are documents that are provided by my website and will always be available to everyone. When a user does not have its own version of a default document, the default document will become the active version for that user by default. This is implicated. For active documents we do NOT insert rows in the vrm_document_active_document table. This means that if there are no versions of a default document active for the user, the default document becomes the active version by default for that user.
When displaying the documents with their versions to the user, I want to show all the versions in a list under each other, where the active version is displayed first, and then the others are sorted on the last modified time.
The following query gives me the correct results for documents that aren't default documents:
SELECT vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1 ) )
ORDER BY vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC
However, as can be seen from the query, if we imagine that document ID 1 to be a default document, all fields in the vrm_document_active_document table will contain NULL, and thus it will sort based on the modified time.
The following is (in pseudo-code) what I need to achieve, is this possible using mariaDB SQL?
ORDER BY:
if all values == NULL then value with default_document is first , then order by modified_utc
else order by modified_utc
DB Fiddle: https://www.db-fiddle.com/f/71TUvdxFgFLhdncgo4BRre/1
EDIT: DB Fiddle added
In MariaDB you have the ifnull function and you can use it as
ifnull(expr1, expr2)
which effectively says:
evaluate expr1 and if it's null, then evaluate expr2
If I understand your problem well, then your solution looks like
order by ifnull(vdad.vrm_document_id, ifnull(vd.timestamp_modified_utc, someothervalue)) desc
In the clause above someothervalue represents any field that you may want to default to. The desirable criteria was unclear to me. However, if you want the default document to be the very first criteria, then
ORDER BY dv.is_default_document desc, vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC
or, if you want that to be the very last criteria, then
ORDER BY vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC, dv.is_default_document desc
EDIT
SELECT vdad.*, vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1 ) )
ORDER BY
CASE not exists (
SELECT 1 as cnt
FROM `vrm_document` AS `vd2`
LEFT JOIN vrm_document_active_document AS vdad2 ON (vd2.vrm_document_id = vdad2.vrm_document_id AND vd2.user_id = vdad2.user_id)
WHERE ((`vd2`.`parent_vrm_document_id` = 1 AND `vd2`.`user_id` IN (2,18,21)) OR (`vd2`.`vrm_document_id` = 1 ) )
AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))
)
WHEN 1 THEN vd.vrm_document_id
ELSE 0 END,
vdad.vrm_document_id DESC,
vd.timestamp_modified_utc DESC
The query above uses case-when-else-end to check for the existence of some records. It is unclear for me what should be the exact criteria, but, even if the example above is not your exact match, then you can replace
AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))
to what you need and it should work.
EDIT2
The current query sorts by the two criteria in the question by default, but if the first two columns are null, then the very first element will be the default document:
SELECT vdad.*, vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1 ) )
ORDER BY
CASE not exists (
SELECT 1 as cnt
FROM `vrm_document` AS `vd2`
LEFT JOIN vrm_document_active_document AS vdad2 ON (vd2.vrm_document_id = vdad2.vrm_document_id AND vd2.user_id = vdad2.user_id)
WHERE ((`vd2`.`parent_vrm_document_id` = 1 AND `vd2`.`user_id` IN (2,18,21)) OR (`vd2`.`vrm_document_id` = 1 ) )
AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))
)
WHEN 1 THEN vd.is_default_document
ELSE 0 END desc,
vdad.vrm_document_id DESC,
vd.timestamp_modified_utc DESC

SQL Selecting count from join returning 0 instead of null result

I have a query that gets data and also joins another table (A) and counts the rows in that join table (B). However if the main table (A) is empty I want the query to return nothing. However it is returning a result of null for id and date and an integer value of 0 for users instead of a null row. How do I get an empty result instead of it returning something?
Returning:
id | date | users
null | null | 0
SQL Code
SELECT
`sessions`.`id`,
`sessions`.`date`,
COUNT( sessions_users.id ) AS users
FROM
`sessions`
LEFT JOIN `sessions_users` ON `sessions`.`id` = `sessions_users`.`sessions_id`
An aggregate query without a group by clause always returns a single record, regardless of the content of the underlying result set (and even if it is empty).
But, since you have non-aggregated columns in the select clause (sessions.id and sessions.date), your query is missing a group by clause anyway. In non-ancient versions in MySQL, where sql mode ONLY_FULL_GROUP_BY is enabled by default, this is a syntax error.
Consider:
SELECT
`sessions`.`id`,
`sessions`.`date`,
COUNT( sessions_users.id ) AS users
FROM
`sessions`
LEFT JOIN `sessions_users` ON `sessions`.`id` = `sessions_users`.`sessions_id`
GROUP BY
`sessions`.`id`,
`sessions`.`date`
This will produce one record per session id and date, along with the count of matching records in sessions_users. If there are no records in sessions, the query will return an empty result set.
If I understand correctly, instead of NULL, you want something like this:
id | date | users
| | 0
If so, just simply use IFNULL() in your SELECT as such:
SELECT
IFNULL(`sessions`.`id`,' ') as id,
IFNULL(`sessions`.`date`,' ') as date,
....
There are also a few other ways to achieve this using just IF() or CASE .. but IFNULL is very straight forward.
BUT if you don't want to see any NULL and 0 values, change your LEFT JOIN to INNER JOIN and you're done.
From your description, it sounds like you want an inner join:
SELECT s.id, s.date, COUNT(*) as users
FROM sessions s JOIN
sessions_users su
ON su.id = su.sessions_id;

Sql select where array in column

In my query I use join table category_attributes. Let's assume we have such rows:
category_id|attribute_id
1|1
1|2
1|3
I want to have the query which suites the two following needs. I have a variable (php) of allowed attribute_id's. If the array is subset of attribute_id then category_id should be selected, if not - no results.
First case:
select * from category_attributes where (1,2,3,4) in category_attributes.attribute_id
should give no results.
Second case
select * from category_attributes where (1,2,3) in category_attributes.attribute_id
should give all three rows (see dummy rows at the beginning).
So I would like to have reverse side of what standard SQL in does.
Solution
Step 1: Group the data by the field you want to check.
Step 2: Left join the list of required values with the records obtained in the previous step.
Step 3: Now we have a list with required values and corresponding values from the table. The second column will be equal to required value if it exist in the table and NULL otherwise.
Count null values in the right column. If it is equal to 0, then it means table contains all the required values. In that case return all records from the table. Otherwise there must be at least one required value is missing in the table. So, return no records.
Sample
Table "Data":
Required values:
10, 20, 50
Query:
SELECT *
FROM Data
WHERE (SELECT Count(*)
FROM (SELECT D.value
FROM (SELECT 10 AS value
UNION
SELECT 20 AS value
UNION
SELECT 50 AS value) T
LEFT JOIN (SELECT value
FROM Data
GROUP BY value) D
ON ( T.value = D.value )) J
WHERE value IS NULL) = 0;
You can use group by and having:
select ca.category_id
from category_attributes ca
where ca.attribute_id in (1, 2, 3, 4)
group by ca.category_id
having count(*) = 4; -- "4" is the size of the list
This assumes that the table has no duplicates (which is typical for attribute mapping tables). If that is a possibility, use:
having count(distinct ca.attribute_id) = 4
You can aggregate attribute_id into array and compare two array from php.
SELECT category_id FROM
(select category_id, group_concat(attribute_id) as attributes from category_attributes
order by attribute_id) t WHERE t.attributes = (1, 2, 3);
But you need to find another way to compare arrays or make sure that array is always sorted.

Use subquery in mysql

The query below gives me 2 out of the 3 answers I'm looking for. On the sub-query select I get null instead of no
the 3 possible values for column name isCyl could be blank, yes, no
I'm not sure if the sub-query is the best way to go about it, but I don't know how else to re-state the query.
The schedule table has a series of columns to show what tasks must be completed on an assignment. Related tables store the results of the tasks if they were assigned to be completed. So I need to test if a specific task was scheduled. If so, then I need to see if the results of the task have been recorded in the related table. For brevity I am only showing one of the columns here.
SELECT s.`reckey`,
if(s.cylinders="T",
(select
if(c.areckey is not null,
"yes",
"no"
)
from cylinders c where c.areckey = s.reckey limit 1
)
,""
) as isCyl
from schedule s
where s.assignmentDate between 20161015 and 20161016
order by s.reckey
Use a LEFT JOIN, which returns NULL for columns in the child table when there's no match.
SELECT s.reckey, IF(s.cylinders = "T",
IF(c.areckey IS NOT NULL, 'yes', 'no'),
"") AS isCyl
FROM schedule AS s
LEFT JOIN cylinders AS c ON c.areckey = s.reckey
WHERE s.assignmentDate between 20161015 and 20161016
ORDER BY s.reckey
If there can be multiple rows in cylinders with the same areckey, change it to:
LEFT JOIN (select distinct areckey FROM cylinders) AS c on c.areckey = s.reckey
or use SELECT DISTINCT in the main query.

Adding a column to a select statement based on the existence of a record in another table

I am writing a SQL query that I would like to add a column to the recordset which has a value based on the existence of a record in another table. I have a left join joining the tables, and I am assuming I have to do some sort of pivot in my SQL, but I am not familiar with table pivoting.
My existing SQL is
SELECT tabs.name,tabs.id AS tabid,tabs.sort,fields.id AS fieldid, fields.label
FROM tabs
INNER JOIN fields
ON tabs.id = fields.tabid
LEFT JOIN fields_reports
ON fields_reports.fieldid = fields.id
WHERE fields_reports.reportid = 57
GROUP BY fields.id
ORDER BY tabs.sort, fields.id
What happened in the SQL is that it pulls field (which is the core of the statement) and the tabs (which are essentailly categories). The fields_reports table maps fields to reports that I am building.
What I need to do is add a column to my statement that says: if the current field has a record in the fields_reports table with the report number passed in (57) then assign the column value of 1, else 0.
Edit: I have another issue with the query. Right now the query is only pulling fields attached to one report. Instead of using a case is there a way that I can do a subquery to select from the fields_reports table so that I can pull all fields and then have the column attaching it to a report?
This is the query that pulls the records now, but only pulls one reports fields
SELECT tabs.name,tabs.id AS tabid,tabs.sort,fields.id AS fieldid, fields.label,
CASE WHEN fields_reports.id IS null THEN 0 ELSE 1 END AS inReport
FROM fields
INNER JOIN tabs
ON tabs.id = fields.tabid
LEFT JOIN fields_reports
ON fields_reports.fieldid = fields.id
WHERE fields_reports.reportid = 57
GROUP BY fields.id
ORDER BY tabs.sort, fields.id
Let me know if I should open up a new question for this.
You can `SELECT ..., IF(field_reports.reportid=57),1,0), ... FROM ...`
EDIT The test on reportid in the WHERE clause will need to be removed, or else this makes little sense as a solution. (Thanks to #Dave Long for this.)
Look what I've done to your script:
SELECT tabs.name,tabs.id AS tabid,tabs.sort,fields.id AS fieldid, fields.label
FROM tabs
INNER JOIN fields
ON tabs.id = fields.tabid
LEFT JOIN fields_reports
ON fields.id = fields.id AND fields_reports.reportid = 57
GROUP BY fields.id
ORDER BY tabs.sort, fields.id
Noticed something different? I dropped the WHERE clause and moved the condition to the ON clause of LEFT JOIN fields_reports. WHERE, as it was specified, turned your LEFT JOIN into INNER JOIN, so the result set would only try to select the rows with fields_reports.reportid = 57. If there were no such rows, none would be returned, i.e. that way you couldn't have your flag column working.
But now it can be defined, for example, like this:
MAX(CASE fields_reports.reportid WHEN 57 THEN 1 ELSE 0 END) AS TheFlagColumn
Just append it to your select list.
The phrase would be:
select ....
, case when LeftJoinedTable.column is null then 0 else 1 end
from ....
this works because, when you left join and no row exists in the joined table, the columns are still present in the result but they are all null. So you can test for this null value to see if you matched to a row in the right side of the join.