SQL Select Where or Having - sql

Im attempting to get some records from a table based on certain factors.
One of the factors is simply with fields on the same table, the other is when joining to another table, I want to compare the number of records in the joined table to a field on the first table. Below is a sample code.
select * from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
-- Having COUNT(LC.ID) = D.Lines
Now from the code above I cant have the Count function in the where clause, and I cant have a field in in the having clause without it being in a function.
Im probably missing something very simple here. But I cant figure it out.
Any help is appreciated it.
EDIT: I do apologise should have explained the structure of the tables, the Destinations are single records, which the LiveCalls table can hold multiple records based on the Destinations ID (foreign key).
Thank you very much for everyones help. My final code:
select D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID, COUNT(LC.ID) AS LiveCalls from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = #ConfigurationID
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID
HAVING COUNT(LC.ID) <= D.Lines

The simple thing you're missing is the GROUP BY statement.
As JNK mentioned in the comments below, you cannot use an aggregate function (such as COUNT, AVG, SUM, MIN) if you don't have a GROUP BY clause, unless your SELECT statement only references literal values (and no column names).
Your code should probably be something like:
SELECT <someFields>
FROM tDestinations D
LEFT JOIN tLiveCalls LC on LC.DestinationID = D.ID
WHERE D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY <someFields>
HAVING COUNT(LC.ID) = D.Lines
Note that you have to specify the selected fields explicitely, in both the SELECT and GROUP BY statements (no * allowed).

you can only use having with aggregations. Actually having is the "where clause" for aggregation, BUT you can still have a where on the columns that you are no aggregating.
For example:
SELECT TABLE_TYPE, COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
where TABLE_TYPE='VIEW'
group by TABLE_TYPE
having COUNT(*)>1
In your case you need to use havving count(*)=1
so, I think your query would be something like this:
select YOUR_COLUMN
from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486 AND (D.Active = 1 AND D.AlternateFail > GETDATE())
group by YOUR_COLUMN
Having COUNT(LC.ID) = value

Related

SQL aggregation updates for some but not others

I am running this query which should take the sum of an amount from a table and if it <= 0, update the status of a different table from Active to Deactive. The query updates some values but not others. I have isolated to one observation where there are 3 payments that total 0 where it does not work.(123456789) What could be happening here? I am using sql query in Microsoft Access. Thank you.
UPDATE tbl_MASTER INNER JOIN tbl_Payments ON tbl_MASTER.DeviceID = tbl_Payments.DeviceID SET tbl_MASTER.ActiveDeactive = "DeActive"
WHERE tbl_Payments.Amount=(SELECT SUM(tbl_Payments.Amount) <= 0 FROM tbl_Payments) AND tbl__MASTER = '123456789';
Your query doesn't really make a lot of sense, to be honest. Where you have tbl_Payments.Amount=(SELECT SUM(tbl_Payments.Amount) <= 0 FROM tbl_Payments), that sub-query will just be summing up the "Amount" of every record in the table, regardless of which DeviceID. Plus, you're looking for one record in tbl_Payments table where the Amount = the sum of all of the Amounts in tbl_Payments??
I'd suggest that your query probably needs to be something more like this:
UPDATE tbl_MASTER SET tbl_MASTER.ActiveDeactive = "DeActive"
WHERE (SELECT SUM(tbl_Payments.Amount) FROM tbl_Payments WHERE tbl_Payments.DeviceID = tbl_MASTER.DeviceID) <= 0 AND tbl__MASTER = '123456789';
Currently, the subquery does not correlate specific IDs to outer query and also you specify <= 0 inside subquery's SELECT clause. Consider adjusting for IN clause with logic in a conditional HAVING and use table aliases to distinguish same named tables.
UPDATE tbl_MASTER AS m
INNER JOIN tbl_Payments AS p
ON m.DeviceID = p.DeviceID
SET m.ActiveDeactive = 'DeActive'
WHERE sub_p.DeviceID IN (
SELECT sub_p.DevideID
FROM tbl_Payments AS sub_p
GROUP BY sub_p.DeviceID
HAVING SUM(sub_p.Amount) <= 0
)

Join SQL Server Showing Duplicate Row

I want to ask something about joining query. I have a query like this:
SELECT b.compilecodingid,
a.subjobfamily,
b.position,
b.nocoding,
( CASE
WHEN (SELECT Count(0)
FROM trlspbia
WHERE learningsystemid = a.learningsystemid
AND compilecodingid = b.compilecodingid
AND moduleid = '2018081616230361362303614'
AND learningroadmap = 'Basic') > 0 THEN 1
ELSE 0
END ) AS CountPickPBIA
FROM trlsplanning a,
trcompilecodingheader b
WHERE a.learningsystemid = b.learningsystemid
AND a.position = b.position
AND a.learningsystemid = '2018081513283162000000001'
order by CountPickPBIA desc
I know it's because Column Position on Table TrLsPlanning has more than 1 data,
Anyone can help me to find the solution?
Thank you.
The simplest solution is probably select distinct:
SELECT cch.compilecodingid, p.subjobfamily, cch.position, cch.nocoding,
(CASE WHEN EXISTS (SELECT 1
FROM trlspbia s
WHERE s.learningsystemid = p.learningsystemid AND
s.compilecodingid = ccb.compilecodingid AND
s.moduleid = '2018081616230361362303614' AND
s.learningroadmap = 'Basic'
)
THEN 1
ELSE 0
END) AS CountPickPBIA
FROM trlsplanning p JOIN
trcompilecodingheader cch
ON p.learningsystemid = cch.learningsystemid AND
p.position = cch.position
WHERE p.learningsystemid = '2018081513283162000000001'
ORDER BY CountPickPBIA DESC;
SELECT DISTINCT incurs its own overhead. But without more information about the structure and contents of the table, this is the simplest solution.
Note other changes in the query:
Table aliases are abbreviations for table names, rather than being arbitrary letters.
The JOIN syntax is fixed, to use modern, proper, and standard JOIN/ON.
All columns are qualified with the table alias, particularly those in the correlated subqueries.
The subquery uses EXISTS rather than COUNT(*). This is both more efficient and it probably better expresses the logic you want.

SQL Inner Join and nearest row to date

I dont't get it. I changed some of the code. In the WPLEVENT Table are a lot of Events per person. In the Persab-Table are the Persons with their History. Now I need the from the Persab Table just that row wich matches the persab.gltab Date nearest to the WPLEVENT.vdat Date. So all rows from the WPLEVENT, but just the one matching row from the PERSAB-Table.
SELECT
persab.name,
persab.vorname,
vdat,
eventstart,
persab.rc1,
persab.rc2
FROM wplevent
INNER JOIN
persab ON WPLEVENT.PersID = persab.PRIMKEY
INNER JOIN
(SELECT TOP 1 persab.rc1
FROM PERSAB
WHERE persab.gltab <= getdate() --/ Should be wplevent.vdat instead of getdate()
) NewTable ON wplevent.persid = persab.primkey
WHERE
persid ='100458'
ORDER BY vdat DESC
Need to use the MAX() function with the proper syntax by supplying an expression like MAX(persab.rc1). Also need to use GROUP BY for the second column rc2 in the subquery (although it looks like you do not need it). Finally you are missing the ON clause for the final INNER JOIN. I can update the answer to fix the query if you provide that information.
SELECT
Z1PERS.NAME
, Z1PERS.VORNAME
, WPLEVENT.VDat
, WPLEVENT.EventStart
, WPLEVENT.EventStop
, WPLEVENT.PEPGROUP
, Z1SGRP.TXXT
, PERSAB.GLTAB
, Z1PERS.PRIMKEY AS Expr1
, PERSAB.PRIMKEY
FROM
Z1PERS
INNER JOIN
WPLEVENT ON Z1PERS.PRIMKEY = WPLEVENT.PersID
INNER JOIN
Z1SGRP ON WPLEVENT.PEPGROUP = Z1SGRP.GRUPPE
INNER JOIN
(
SELECT MAX(Persab.rc1) --Fixed MAX expression
, persab.rc2
FROM
persab
GROUP BY
persab.rc2 --Need to group on rc2 if you want that column in the query otherwise remove this AND the rc2 column from select list
WHERE
WPLEVENT.PersID = PERSAB.PRIMKEY
AND WPLEVENT.VDat <= PERSAB.GLTAB
) --Missing ON clause for the INNER JOIN here
WHERE z1pers.vorname = 'henning'

Selecting ONLY Duplicates from a joined tables query

I have the following query that I'm trying to join two tables matching their ID so I can get the duplicated values in "c.code". I've tried a lot of queries but nothing works. I have a 500k rows in my database and with this query I only get 5k back, which is not right. Im positive it's at least 200K. I also tried to use Excel but it's too much for it to handle.
Any ideas?
Thanks in advance, everyone.
SELECT c.code, c.name as SCT_Name, t.name as SYNONYM_Name, count(c.code)
FROM database.Terms as t
join database.dbo.Concepts as c on c.ConceptId = t.ConceptId
where t.TermTypeCode = 'SYNONYM' and t.ConceptTypeCode = 'NAME_Code' and c.retired = '0'
Group by c.code, c.name, t.name
HAVING COUNT(c.code) > = 1
Order by c.code
with data as (
select c.code, c.name as SCT_Name, t.name as SYNONYM_Name
from database.Terms as t inner join database.dbo.Concepts as c
on c.ConceptId = t.ConceptId
where
t.TermTypeCode = 'SYNONYM'
and t.ConceptTypeCode = 'NAME_Code'
and c.retired = '0'
)
select *
--, (select count(*) from data as d2 where d2.code = data.code) as code_count
--, count(*) over (partition by code) as code_count
from data
where code in (select code from data group by code having count(*) > 1)
order by code
If you want just duplicates of c.code, your Group By is wrong (and so is your Having clause). Try this:
SELECT c.code
FROM database.Terms as t
join database.dbo.Concepts as c on c.ConceptId = t.ConceptId
where t.TermTypeCode = 'SYNONYM' and t.ConceptTypeCode = 'NAME_Code' and c.retired = '0'
Group by c.code
HAVING COUNT(c.code) > 1
This will return all rows where you have more than one c.code value.
You need to use INTERSECT instead of JOIN. Basically you perform the select on the first table then intersect with the second table. The result is the duplicate rows.
Only select the id column, though, otherwise the intersect won't work as expected.

MS-Access -> SELECT AS + ORDER BY = error

I'm trying to make a query to retrieve the region which got the most sales for sweet products. 'grupo_produto' is the product type, and 'regiao' is the region. So I got this query:
SELECT TOP 1 r.nm_regiao, (SELECT COUNT(*)
FROM Dw_Empresa
WHERE grupo_produto='1' AND
cod_regiao = d.cod_regiao) as total
FROM Dw_Empresa d
INNER JOIN tb_regiao r ON r.cod_regiao = d.cod_regiao ORDER BY total DESC
Then when i run the query, MS-Access asks for the "total" parameter. Why it doesn't consider the newly created 'column' I made in the select clause?
Thanks in advance!
Old Question I know, but it may help someone knowing than while you cant order by aliases, you can order by column index. For example, this will work without error :
SELECT
firstColumn,
IIF(secondColumn = '', thirdColumn, secondColumn) As yourAlias
FROM
yourTable
ORDER BY
2 ASC
The results would then be ordered by the values found in the second column wich is the Alias "yourAlias".
Aliases are only usable in the query output. You can't use them in other parts of the query. Unfortunately, you'll have to copy and paste the entire subquery to make it work.
You can do it like this
select * from(
select a + b as c, * from table)
order by c
Access has some differences compared to Sql Server.
Why it doesn't consider the newly
created 'column' I made in the select
clause?
Because Access (ACE/Jet) is not compliant with the SQL-92 Standard.
Consider this example, which is valid SQL-92:
SELECT a AS x, c - b AS y
FROM MyTable
ORDER
BY x, y;
In fact, x and y the only valid elements in the ORDER BY clause because all others are out of scope (ordinal numbers of columns in the SELECT clause are valid though their use id deprecated).
However, Access chokes on the above syntax. The equivalent Access syntax is this:
SELECT a AS x, c - b AS y
FROM MyTable
ORDER
BY a, c - b;
However, I understand from #Remou's comments that a subquery in the ORDER BY clause is invalid in Access.
Try using a subquery and order the results in an outer query.
SELECT TOP 1 * FROM
(
SELECT
r.nm_regiao,
(SELECT COUNT(*)
FROM Dw_Empresa
WHERE grupo_produto='1' AND cod_regiao = d.cod_regiao) as total
FROM Dw_Empresa d
INNER JOIN tb_regiao r ON r.cod_regiao = d.cod_regiao
) T1
ORDER BY total DESC
(Not tested.)
How about:
SELECT TOP 1 r.nm_regiao
FROM (SELECT Dw_Empresa.cod_regiao,
Count(Dw_Empresa.cod_regiao) AS CountOfcod_regiao
FROM Dw_Empresa
WHERE Dw_Empresa.[grupo_produto]='1'
GROUP BY Dw_Empresa.cod_regiao
ORDER BY Count(Dw_Empresa.cod_regiao) DESC) d
INNER JOIN tb_regiao AS r
ON d.cod_regiao = r.cod_regiao
I suggest using an intermediate query.
SELECT r.nm_regiao, d.grupo_produto, COUNT(*) AS total
FROM Dw_Empresa d INNER JOIN tb_regiao r ON r.cod_regiao = d.cod_regiao
GROUP BY r.nm_regiao, d.grupo_produto;
If you call that GroupTotalsByRegion, you can then do:
SELECT TOP 1 nm_regiao, total FROM GroupTotalsByRegion
WHERE grupo_produto = '1' ORDER BY total DESC
You may think it's extra work to create the intermediate query (and, in a sense, it is), but you will also find that many of your other queries will be based off of GroupTotalsByRegion. You want to avoid repeating that logic in many other queries. By keeping it in one view, you provide a simplified route to answering many other questions.
How about use:
WITH xx AS
(
SELECT TOP 1 r.nm_regiao, (SELECT COUNT(*)
FROM Dw_Empresa
WHERE grupo_produto='1' AND
cod_regiao = d.cod_regiao) as total
FROM Dw_Empresa d
INNER JOIN tb_regiao r ON r.cod_regiao = d.cod_regiao
) SELECT * FROM xx ORDER BY total