Pulling my hair with this Syntax Error - sql

I seem to be running into a query syntax error but cannot seem to isolate it. I am using MS Access and when I run the queries I get a syntax error in FROM clause.
I have two tables and they are in a one to many relationship:
Table 1 called (customer) with the following fields:
ID
FirstName
Table 2 called (tblservice) with the following fields:
serviceID
Timing
Total
customerID <-Foreign Key
First Query:
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
group by s.id
Second Query(30 day span):
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by s.id

Try this:
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
group by c.id, c.firstname
and
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by c.id, c.firstname
You cannot select c.id and c.firstname unless you group by them. And you can use count(c.id) since you are already grouping by c.id. I have not used SQL in MS Access though. So I am not 100% sure. Try it out.

MS Access may require you to use INNER JOIN instead of just JOIN.

Related

How to use subquery in WHERE? (AdventureWorks)

What am I doing wrong? I need to make sure my column 'OrderCount' only shows values more than 20. Please note: I am not allowed to use JOIN in this case.
SELECT FirstName, LastName, (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE SalesOrderHeader.ContactID = Contact.ContactID) AS OrderCount
FROM Contact
WHERE COUNT(SalesOrderID) = (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE COUNT(SalesOrderID) > 20
Basically, you would need to correlate the subquery in the where clause as well - yours count all orders, not those of the concerned customers only.
But a subquery seems simpler:
SELECT *
FROM (
SELECT c.FirstName, c.LastName,
(
SELECT COUNT(s.SalesOrderID)
FROM SalesOrderHeader s
WHERE s.ContactID = c.ContactID
) AS OrderCount
FROM Contact c
) t
where OrderCount > 20
Please note: I am not allowed to use JOIN in this case.
This really is a contrived example. In real life situation, a join with outer aggregation and a having clause, or a lateral join, would be more appropriate:
SELECT c.FirstName, c.LastName, o.OrderCount
FROM Contact c
CROSS APPLY (
SELECT COUNT(s.SalesOrderID) as OrderCount
FROM SalesOrderHeader s
WHERE s.ContactID = c.ContactID
) AS o
where o.OrderCount > 20
SELECT FirstName, LastName, (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE SalesOrderHeader.ContactID = Contact.ContactID) AS OrderCount
FROM Contact
WHERE Contact.ContactID in (SELECT
SalesOrderHeader.ContactID
FROM SalesOrderHeader
Group by
SalesOrderHeader.ContactID
Having COUNT(*) > 20
The problem here isn't the subquery, it's the aggregate function (the COUNT in this case) that's causeing the problem. You can't put an aggregate function that references the tables in the FROM in the WHERE you would need to reference it in the HAVING.
I think you can also condense this right down to not need a subquery:
SELECT C.FirstName,
C.LastName
FROM dbo.Contact C
JOIN dbo.SalesOrderHeader SOH ON C.ContactID = SOH.ContactID
GROUP BY C.FirstName,
C.LastName
HAVING COUNT(SalesOrderID) > 20;

Need your help in SQL query

There are two tables:
Clients (id, name)
Order (id, id_client, name), where id_client - foreign key.
Write a query that selects the identifier and name of the first table and the number of records in the second table, associated with them. The result should be sorted by surname in descending order.
I've tried
SELECT
Clients.id, Clients.name, count(id)
FROM clients
INNER JOIN Order ON Clients.id = Order.id_client
GROUP BY
Clients.id, Clients.name
ORDER BY
Clients.name DESC
But it doesn't work. What is wrong?
SELECT
c.ID,
c.Name,
COUNT(o.ID)
FROM
Clients c
LEFT JOIN [Order] o
ON
o.id_client = c.id
GROUP BY
c.ID,
c.Name
ORDER BY
c.Name DESC
SELECT Clients.id, Clients.name, count(client.id) FROM clients INNER JOIN Order on Clients.id=Order.id_client GROUP BY Clients.id, Clients.name ORDER BY Clients.name DESC
Change count(id) to
count(Clients.id) or count(Order.id)
I don't know which table you need count(id) from. I hope you understand where the issue is.
SELECT
c.ID,
c.Name,
COUNT(o.ID)
FROM
Clients c,
Order o
WHERE o.id_client = c.id
GROUP BY
c.ID
c.Name

Need hints on seemingly simple SQL query

I'm trying to do something like:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
However, SQL will not allow the COUNT function. The error given at execution is that c.Id is not valid in the select list because it isn't in the group by clause or isn't aggregated.
I think I know the problem, COUNT just counts all the rows in the orders table. How can I make a count for each customer?
EDIT
Full query, but it's in dutch... This is what I tried:
select k.ID,
Naam,
Voornaam,
Adres,
Postcode,
Gemeente,
Land,
Emailadres,
Telefoonnummer,
count(*) over (partition by k.id) as 'Aantal bestellingen',
Kredietbedrag,
Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
from klanten k
join bestellingen on bestellingen.klantId = k.id
No errors but no results either..
When using an aggregate function like that, you need to group by any columns that aren't aggregates:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
GROUP BY c.id, c.name
If you really want to be able to select all of the columns in Customers without specifying the names (please read this blog post in full for reasons to avoid this, and easy workarounds), then you can do this lazy shorthand instead:
;WITH o AS
(
SELECT CustomerID, CustomerCount = COUNT(*)
FROM dbo.Orders GROUP BY CustomerID
)
SELECT c.*, o.OrderCount
FROM dbo.Customers AS c
INNER JOIN dbo.Orders AS o
ON c.id = o.CustomerID;
EDIT for your real query
SELECT
k.ID,
k.Naam,
k.Voornaam,
k.Adres,
k.Postcode,
k.Gemeente,
k.Land,
k.Emailadres,
k.Telefoonnummer,
[Aantal bestellingen] = o.klantCount,
k.Kredietbedrag,
k.Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
FROM klanten AS k
INNER JOIN
(
SELECT klantId, klanCount = COUNT(*)
FROM dbo.bestellingen
GROUP BY klantId
) AS o
ON k.id = o.klantId;
I think this solution is much cleaner than grouping by all of the columns. Grouping on the orders table first and then joining once to each customer row is likely to be much more efficient than joining first and then grouping.
The following will count the orders per customer without the need to group the overall query by customer.id. But this also means that for customers with more than one order, that count will repeated for each order.
SELECT c.id, c.name, COUNT(orders.id) over (partition by c.id)
FROM customers c
JOIN orders ON o.customerId = c.id

Using an inner select construction for a select value

I would like to hear if anyone can tell me a simple syntax that accomplishes the same as the following (with the same flexibility):
SELECT C.CompanyName,
(SELECT Count(*) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
Now, what's important is that the inner SELECT giving the EmployeeCount is:
An independent SELECT statement
This means that it should work with any existing SELECT, even if it already contains joins etc.
Can use values from the parent SELECT
I know that this scenario can be easily accomplished in other ways, but the above is a simplified example to explain the challenge. My real scenario is a complex SELECT statement where I do not want to complicate it by adding more joins. Performance is no issue.
Using INNER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
INNER JOIN Employees E on E.CompanyId = C.Id
Using NESTED JOIN:
SELECT C.CompanyName, Count(E.1) as EmployeeCount
FROM Company C, Employess E
WHERE E.CompanyId = C.Id
If you want to use the same syntax, at least put this:
SELECT C.CompanyName,
(SELECT Count(1) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
If you need all the data to be shown, even the ones the companies without any Employees, you can use a LEFT OUTER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
LEFT OUTER JOIN Employees E on E.CompanyId = C.Id
Try using a derived table, which statifies both your conditions.
An independent SELECT statement.
a. Using a Derived Table allows you to keep your independent Select Statement
Can use values from the parent SELECT.
a. As an Inner join you can still use values from the parent select.
SELECT
C.CompanyName,
EC.EmployeeCount
FROM Company C
INNER JOIN (SELECT
Count(*) AS EmployeeCount
FROM Employees ) EC
ON WHERE EC.CompanyId = C.Id
If your inner select is complicated, then why not make a view of it:
CREATE VIEW EmpSelect AS
SELECT CompanyId, whatever FROM Employees;
Then
SELECT
C.CompanyName, Count(*) AS EmpCount
FROM
Company C
LEFT JOIN EmpSelect E
ON C.Id = E.CompanyId
GROUP BY
C.CompanyName;

ORA-00979: not a GROUP BY expression? [duplicate]

This question already has answers here:
Oracle ORA-00979 - "not a GROUP BY expression"
(4 answers)
Closed 8 years ago.
I have found the solution to this, but what in case one of the column is a subquery, how can i include it in group by, or do i need to include that in group by. I will paste the query here..
SELECT s.customerid, s.denomid,
(SELECT su.quantity
FROM stockupdations su
WHERE s.customerid = su.custid
AND s.denomid = su.denomid
AND s.curid = su.curid) AS cur_stock, c.name AS cus_name, d.denomname AS denom,
cur.curcode AS currency
FROM stock s
LEFT JOIN customers c
ON s.customerid = c.custid
LEFT JOIN denomination d
ON d.denomid = s.denomid
LEFT JOIN currency cur
ON cur.curid = s.curid
GROUP BY s.denomid, s.customerid, c.name, d.denomname, cur.curcode
ORDER BY s.customerid ASC
What about a WITH statement?
WITH tmp AS
(
SELECT s.customerid, s.denomid,
c.name AS cus_name,
d.denomname AS denom,
cur.curcode AS currency
FROM stock s
LEFT JOIN customers c
ON s.customerid = c.custid
LEFT JOIN denomination d
ON d.denomid = s.denomid
LEFT JOIN currency cur
ON cur.curid = s.curid
GROUP BY s.denomid, s.customerid, c.name, d.denomname, cur.curcode
ORDER BY s.customerid ASC
)
SELECT tmp.customerid, tmp.denomid,
su.quantity,
tmp.cus_name,
tmp.denom,
tmp.currency
FROM tmp
INNER JOIN stockupdations su
ON tmp.customerid = su.custid
AND tmp.denomid = su.denomid
AND tmp.curid = su.curid
You can use your "Inner query" in the from clause than on the select.
Say I have a CUSTOMER table and ORDER table,
I can have something like
SELECT C.CUSTOMER_ID, COUNT(T.ORDER_ID)
FROM CUSTOMERS C
JOIN (SELECT CUSTOMER_ID, ORDER_ID, ORDER_DATE, ORDER_STATUS FROM ORDERS O WHERE O.STATUS <> 'DECLINED') T
ON T.CUSTOMER_ID = C.CUSTOMER ID
GROUP BY C.CUSTOMER_ID
(This SQL is just an example, and I know there are better ways to write this, but I could not think of any other example immediately)
You don't have to do everything at once. Try breaking your query into multiple pieces. Subqueries, analytic functions, or other complicated logic will look like simple rows to the outer query. (Don't worry about performance, Oracle will re-write it and do everything as one step if it makes sense.)
--Step 3
select [simple values]
from
(
--Step 2
select [insanity]
from
(
--Step 1
select [madness]
from
[impossible joins]
)
)
group by [simple values]