Is there a simpler way to perform this query?
Actually using this in part of a larger query
Would rather not use EXCEPT, UNION, INTERSECT
As part of the more larger query the optimizer can get stupid on the derived table and except
Not of value to post the larger query as it is dynamic
The PK on docSVsys is sID
The PK on docMVenum1 is sID, enumID, valueID
select sID from docSVsys
EXCEPT
select sID
from docMVenum1
where enumID = 140
and valueID in (1,2)
group by sID
having count(*) = 2
select docSVsys.sID from docSVsys
left outer join
( select sID
from docMVenum1
where enumID = 140
and valueID in (1,2)
group by sID
having count(*) = 2 ) as joinTable
on docSVsys.sID = joinTable.sID
where joinTable.sID is null
I know the two queries are the same
I am looking for a 3rd simpler
I believe the IN operator might cause an inefficiency. Try this:
select sID
from docSVsys
EXCEPT
select sID
from (
select d1.sID
from docMVenum1 d1
join docMVenum1 d2
on d1.sID = d2.sID
where d1.enumID = 140 and d1.valueID = 1
and d2.enumID = 140 and d2.valueID = 2
) T
do you absolutely need the aggregation? cant you just do the grouping and aggregation on the data as created tables, that would be a performance booster.
CREATE TABLE new_table
AS (select sID
from docMVenum1 int
where enumID = 140
and valueID in (1,2)
the only query you would then have to run is the below.
select * from new_table where count(*) = 2
group by sID
Related
I'm trying to find all IDs in TableA that are mentioned by a set of records in TableB and that set if defined in Table C. I've come so far to the point where a set of INNER JOIN provide me with the following result:
TableA.ID | TableB.Code
-----------------------
1 | A
1 | B
2 | A
3 | B
I want to select only the ID where in this case there is an entry for both A and B, but where the values A and B are based on another Query.
I figured this should be possible with a GROUP BY TableA.ID and HAVING = ALL(Subquery on table C).
But that is returning no values.
Since you did not post your original query, I will assume it is inside a CTE. Assuming this, the query you want is something along these lines:
SELECT ID
FROM cte
WHERE Code IN ('A', 'B')
GROUP BY ID
HAVING COUNT(DISTINCT Code) = 2;
It's an extremely poor question, but you you probably need to compare distinct counts against table C
SELECT a.ID
FROM TableA a
GROUP BY a.ID
HAVING COUNT(DISTINCT a.Code) = (SELECT COUNT(*) FROM TableC)
We're guessing though.
I have 3 postgresql tables : Documents, Keywords and a join table.
I have query that searches document.id and document.date if certain keywords are related to that document. That works fine like so:
SELECT
documents.id, documents.document_date
FROM
documents
INNER JOIN
documents_keywords ON documents_keywords.document_id = documents.id
INNER JOIN
keywords ON keywords.id = documents_keywords.keyword_id
WHERE
keywords.keyword IN ('bread' , 'cake')
GROUP BY documents.id
This returns:
id | document_date
----+-----------
4 | 1200
12 | 1280
(2 rows)
I also want to exclude keywords. I thought I could do NOT IN like so:
SELECT
documents.id, documents.document_date
FROM
documents
INNER JOIN
documents_keywords ON documents_keywords.document_id = documents.id
INNER JOIN
keywords ON keywords.id = documents_keywords.keyword_id
WHERE
keywords.keyword NOT IN ('cranberries')
GROUP BY documents.id
But that always returns empty, whatever keyword I put:
id | document_date
----+-----------
(0 rows)
This is incorrect. I expected:
id | document_date
----+-----------
4 | 1200
(1 row)
You might want to use an array expression, like this:
WHERE keyword = any(array['bread', 'cake'])
when you want to include a row.
If you want to exclude something, you have to do the NOT IN over a subselect of the inverse condition, e.g.
SELECT ... WHERE document_id NOT IN
(SELECT document_id FROM ...joins... WHERE keyword = ANY(array['cranberry']))
Here is an example I put together:
WITH documents(d_id, date) AS (
VALUES(1,'1000'),(2,'2000'),(3,'3000'),(4,'4000')
),
keywords(k_id, keyword) AS (
VALUES(1, 'cake'), (2, 'bread'), (3, 'cranberry')
),
documents_keywords (d_id, k_id) AS (
VALUES(1,1),(1,2),(2,2),(2,3),(3,3)
)
SELECT * FROM documents where d_id NOT IN (
SELECT d_id FROM
documents
JOIN documents_keywords USING(d_id)
JOIN keywords USING(k_id)
WHERE keyword = ANY(array['cranberry'])
)
Also, I am not sure why you are using GROUP BY, I don't think you need it.
I've got two tables in my database. One of them, 'orders', contains a set of columns with an integer which represents what the order should contain (like 5 of A and 15 of B). The second table, 'production_work', contains those same order columns, and a date, so whenever somebody completes part of an order, I track it.
So now i need a fast way to know which orders are completed, and I'm hoping to avoid a 'completed' table on the first column as orders are editable and it's just more logic to keep correct.
This query works, but it's horribly written. What's a better way to do this? There are actually 12 of these columns that go into this query...I'm just showing 3 of them for the example.
SELECT *
FROM orders o
WHERE ud = (SELECT SUM(ud) FROM production_work WHERE order_id = o.ident)
AND dp = (SELECT SUM(dp) FROM production_work WHERE order_id = o.ident)
AND swrv = (SELECT SUM(swrv) FROM production_work WHERE order_id = o.ident)
select o.*
from
orders o
inner join
(
select order_id, sum(ud) as ud, sum(dp) as dp, sum(swrv) as swrv
from production_work
group by order_id
) pw on o.ident = pw.order_id
where
o.ud = pw.ud
and o.dp = pw.dp
and o.swrv = pw.swrv
Currently I've a query as follows:
-- Query 1
SELECT
acc_code, acc_name, alias, LAmt, coalesce(LAmt,0) AS amt
FROM
(SELECT
acc_code, acc_name, alias,
(SELECT
(SUM(cr_amt)-SUM(dr_amt))
FROM
ledger_mcg l
WHERE
(l.acc_code LIKE a.acc_code + '.%' OR l.acc_code=a.acc_code)
AND
fy_id=1
AND
posted_date BETWEEN '2010-01-01' AND '2011-06-02') AS LAmt
FROM
acc_head_mcg AS a
WHERE
(acc_type='4')) AS T1
WHERE
coalesce(LAmt,0)<>0
Query 2 is same as Query 1 except that acc_type = '5' in Query 2. Query 2 always returns a resultset with a single row. Now, I need the union of the two queries i.e
Query 1
UNION
Query 2
only when the amt returned by Query 2 is less than 0. Else, I don't need a union but only the resulset from Query 1.
The best way I can think of is to create a parameterised scalar function. How best can I do this?
You could store the result of the first query into a temporary table, then, if the table wasn't empty, execute the other query.
IF OBJECT_ID('tempdb..#MultipleQueriesResults') IS NOT NULL
DROP TABLE #MultipleQueriesResults;
SELECT
acc_code, acc_name, alias, LAmt, coalesce(LAmt,0) AS amt
INTO #MultipleQueriesResults
FROM
(SELECT
acc_code, acc_name, alias,
(SELECT
(SUM(cr_amt)-SUM(dr_amt))
FROM
ledger_mcg l
WHERE
(l.acc_code LIKE a.acc_code + '.%' OR l.acc_code=a.acc_code)
AND
fy_id=1
AND
posted_date BETWEEN '2010-01-01' AND '2011-06-02') AS LAmt
FROM
acc_head_mcg AS a
WHERE
(acc_type='4')) AS T1
WHERE
coalesce(LAmt,0)<>0;
IF NOT EXISTS (SELECT * FROM #MultipleQueriesResults)
… /* run Query 2 */
tblUserProfile - I have a table which holds all the Profile Info (too many fields)
tblMonthlyProfiles - Another table which has just the ProfileID in it (the idea is that this table holds 2 profileids which sometimes become monthly profiles (on selection))
Now when I need to show monthly profiles, I simply do a select from this tblMonthlyProfiles and Join with tblUserProfile to get all valid info.
If there are no rows in tblMonthlyProfile, then monthly profile section is not displayed.
Now the requirement is to ALWAYS show Monthly Profiles. If there are no rows in monthlyProfiles, it should pick up 2 random profiles from tblUserProfile. If there is only one row in monthlyProfiles, it should pick up only one random row from tblUserProfile.
What is the best way to do all this in one single query ?
I thought something like this
select top 2 * from tblUserProfile P
LEFT OUTER JOIN tblMonthlyProfiles M
on M.profileid = P.profileid
ORder by NEWID()
But this always gives me 2 random rows from tblProfile. How can I solve this ?
Try something like this:
SELECT TOP 2 Field1, Field2, Field3, FinalOrder FROM
(
select top 2 Field1, Field2, Field3, FinalOrder, '1' As FinalOrder from tblUserProfile P JOIN tblMonthlyProfiles M on M.profileid = P.profileid
UNION
select top 2 Field1, Field2, Field3, FinalOrder, '2' AS FinalOrder from tblUserProfile P LEFT OUTER JOIN tblMonthlyProfiles M on M.profileid = P.profileid ORDER BY NEWID()
)
ORDER BY FinalOrder
The idea being to pick two monthly profiles (if that many exist) and then 2 random profiles (as you correctly did) and then UNION them. You'll have between 2 and 4 records at that point. Grab the top two. FinalOrder column is an easy way to make sure that you try and get the monthly's first.
If you have control of the table structure, you might save yourself some trouble by simply adding a boolean field IsMonthlyProfile to the UserProfile table. Then it's a single table query, order by IsBoolean, NewID()
In SQL 2000+ compliant syntax you could do something like:
Select ...
From (
Select TOP 2 ...
From tblUserProfile As UP
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Order By NewId()
) As RandomProfile
Union All
Select MP....
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From (
Select TOP 1 ...
From tblUserProfile As UP
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
Order By NewId()
) As RandomProfile
Using SQL 2005+ CTE you can do:
With
TwoRandomProfiles As
(
Select TOP 2 ..., ROW_NUMBER() OVER ( ORDER BY UP.ProfileID ) As Num
From tblUserProfile As UP
Order By NewId()
)
Select MP.Col1, ...
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From TwoRandomProfiles
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Union All
Select ...
From TwoRandomProfiles
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
And Num = 1
The CTE has the advantage of only querying for the random profiles once and the use of the ROW_NUMBER() column.
Obviously, in all the UNION statements the number and type of the columns must match.