select ac1.ACCT_CODE,
ac1.PERIOD,
ac1.MONTH,
ac1.YEAR,
ac1.PRD_BDGT,
ac2.ACCT_CODE,
ac2.PERIOD,
ac2.MONTH,
ac2.YEAR,
ac2.PRD_BDGT
from account ac1, account ac2
where ac1.acct_code='075200'
and ac1.year=1994
and ac1.period between 1 and 6
and ac2.acct_code=ac1.acct_code
and ac2.year=1995
and ac2.period =ac1.period
union
select ac3.ACCT_CODE,
ac3.PERIOD,
ac3.MONTH,
ac3.YEAR,
ac3.PRD_BDGT,
ac4.ACCT_CODE,
ac4.PERIOD,
ac4.MONTH,
ac4.YEAR,
ac4.PRD_BDGT
from account ac3, account ac4
where ac3.acct_code='075200'
and ac3.year=1995
and ac3.period between 7 and 12
and ac4.acct_code=ac3.acct_code
and ac4.year=1996
and ac4.period=ac3.period
Use an OR:
select ac1.ACCT_CODE,
ac1.PERIOD,
ac1.MONTH,
ac1.YEAR,
ac1.PRD_BDGT,
ac2.ACCT_CODE,
ac2.PERIOD,
ac2.MONTH,
ac2.YEAR,
ac2.PRD_BDGT
from account ac1, account ac2
where ac1.acct_code='075200'
and ac2.acct_code=ac1.acct_code
and ac2.period =ac1.period
and ((ac1.year=1994
and ac1.period between 1 and 6
and ac2.year=1995
) OR
(ac1.year=1995
and ac1.period between 7 and 12
and ac2.year=1996))
Your query is taking the union of two very similar queries, where the only difference is certain conditions in the where clause. You can combine them pretty easily by using or in the where clause.
The following query also fixes the join syntax:
select ac1.ACCT_CODE, ac1.PERIOD, ac1.MONTH, ac1.YEAR, ac1.PRD_BDGT, ac2.ACCT_CODE,
ac2.PERIOD, ac2.MONTH, ac2.YEAR, ac2.PRD_BDGT
from account ac1 join
account ac2
on ac2.period = ac1.period and
ac2.acct_code = ac1.acct_code
where ac1.acct_code='075200' and
((ac1.year = 1994 and
ac1.period between 1 and 6
ac2.year=1995
) or
(ac1.year=1995 and
ac1.period between 7 and 12 and
ac2.year=1996
)
);
I would be surprised if this query actually solves your business problem. Doing a self-join on the accounts table is suspicious. Often, an aggregation is what one needs, but I cannot tell the purpose of the query.
Related
In MS Access 365, I have two tables (TClients & TPlans) that I am trying to combine into a single Query (QClientsExtended). Each client could have many or no associated entries on the TPlan list. In my final query, I would like it to list every client regardless of wether or not they have a plan, and give me the date and the details of the most recent plan, if there is one.
I've read all the relevant W3C reference pages, and looked at so many possible solutions, and i've struggled to turn them into something that works for this. It feels like it should be SO simple, I appreciate I'm probably missing a fundamental aspect of SQL coding.
TClients
ClientID ClientFullName ExternalAppts
1 Testy McTestFace 1
2 Clemence Closed 0
3 Nancy New Ref 3
4 Juan One Appt 0
TPlans
PlanID ClientID PlanDetails PlanDate
1 3 Plan 1 05-Dec-22
2 3 Plan 2 10-Dec-22
3 1 plan 10-Dec-22
4 4 nil 05-Dec-22
Qclients Extended
ClientID ClientFullName PlanDetails PlanDate ExternalAppts
1 Testy McTestFace Plan 2 10-Dec-22 1
2 Clemence Closed 0
3 Nancy New Ref plan 10-Dec-22 3
4 Juan One Appt nil 05-Dec-22 0
I've found a solution, but it feels incredibly clunky. I have made two Queries, one to find the most recent date for each client, and the second to Left Join this with the Client Table. I'm sure this should be doable in a single query, but maybe it can't be done in Access. Here are my two Queries:
QlastPlan
SELECT t1.*
FROM TPlans AS t1
INNER JOIN (
SELECT [ClientID],
MAX(PlanDate) AS LastPlan
FROM TPlans
GROUP BY [ClientID])
AS t2 ON (t1.[PlanDate] = t2.LastPlan) AND (t1.[ClientID] = t2.[ClientID]);
QClients
SELECT
TC.*,
QLP.PlanDetails,
QLP.PlanDate,
FROM TClients TC
LEFT JOIN QlastPlan QLP on TC.ClientID = QLP.ClientID;
So perhaps there's another option someone can suggest, but this is what I will run with for now.
Try with a Left Join:
SELECT
TClients.ClientID,
TClients.ClientFullName,
TPlans.PlanDetails,
TPlans.PlanDate,
TClients.ExternalAppts
FROM
TClients
LEFT JOIN
TPlans ON TClients.ClientID = TPlans.ClientID
GROUP BY
TClients.ClientID,
TClients.ClientFullName,
TPlans.PlanDetails,
TPlans.PlanDate,
TClients.ExternalAppts
HAVING
TPlans.PlanDate=
(Select Max(PlanDate) From [TPlans] As T Where T.ClientID = TClients.[ClientID])
OR
TPlans.PlanDate Is Null
I have below given query which is working fine but I want to use "Not In" operator instead of "In" but its giving no results:
SELECT DISTINCT OrderProdDetails.Priority
FROM OrderProdDetails
WHERE (((OrderProdDetails.Priority) In (SELECT DISTINCT OrderProdDetails.Priority
FROM OrderProdDetails WHERE (((OrderProdDetails.OrdID)=[Forms]![UpdateOrder]![OdrID])))));
Desired Query:
SELECT DISTINCT OrderProdDetails.Priority
FROM OrderProdDetails
WHERE (((OrderProdDetails.Priority) Not In (SELECT DISTINCT OrderProdDetails.Priority
FROM OrderProdDetails WHERE (((OrderProdDetails.OrdID)=[Forms]![UpdateOrder]![OdrID])))));
Basically it is referencing a control on parent form and based on that in a subform I want to populate the priority numbers i.e 1,2,3 and if for that record 1 is entered I want to get only 2 and 3 as drop-down option.
ReocordID OrdID Brand Name Priority
2 1 Org 1 2
3 2 Org 2 1
4 1 Org 1 1
6 1 Org 1 3
7 3 Org 3 1
8 4 Org 1 1
9 5 Org 2 1
10 5 Org 2 2
11 6 Org 1 1
12 6 Org 2 2
If there is any other better approach for the same please suggest.
Thanks in advance for your help.
In all likelihood, your problem is that Priority can take on NULL values. In that case, NOT IN doesn't work as expected (although it does work technically). The usual advice is to always use NOT EXISTS with subqueries rather than NOT IN.
But, in your case, I would suggest conditional aggregation instead:
SELECT opd.Priority
FROM OrderProdDetails as opd
GROUP BY opd.Priority
HAVING SUM(IIF(opd.OrdID = [Forms]![UpdateOrder]![OdrID], 1, 0)) = 0;
The HAVING clause counts the number of times the forms OdrId is in the orders. The = 0 means it is never there. Plus, you no longer need a select distinct.
Thanks for your prompt answers however I figured out what the problem was and the answer to problem is.
SELECT DISTINCT OrderProdDetails.Priority
FROM OrderProdDetails
WHERE (((OrderProdDetails.Priority) Not In (SELECT OrderProdDetails.Priority
FROM OrderProdDetails WHERE (((OrderProdDetails.OrdID)=[Forms]![UpdateOrder]![OdrID])
and ((OrderProdDetails.Priority) Is not null) ))));
I realized that the problem was happening only to those where there was a null value in priority so I puth the check of not null and it worked fine.
Thanks
I have user data:
user store item cost
1 10 100 5
1 10 101 3
1 11 102 7
2 10 101 3
2 12 103 4
2 12 104 5
I want a table which will tell me for each user how much he bought from each store and how much he bought in total:
user store cost_this_store cost_total
1 10 8 15
1 11 7 15
2 10 3 12
2 12 9 12
I can do this with two group by and a join:
select s.user, s.store, s.cost_this_store, u.cost_total
from (select user, store, sum(cost) as cost_this_store
from my_data
group by user, store) s
join (select user, sum(cost) as cost_total
from my_data
group by user) u
on s.user = u.user
However, this is definitely not how I would do this if I were writing this in any other language (join is clearly avoidable, and the two group by are not independent).
Is it possible to avoid the join in sql?
PS. I need the solution to work in hive.
You can do this with a windowing function... which Hive added support for last year:
select distinct
user,
store,
sum(cost) over (partition by user, store) as cost_this_store,
sum(cost) over (partition by user) as cost_total
from my_data
However, I'd argue that there wasn't anything glaringly wrong with your original implementation. You've essentially got two different sets of data, which you're combining through a JOIN.
The duplication might look like a code smell in a different language, but this isn't necessarily the wrong approach in SQL, and often you'll have to take approaches such as this that duplicate a portion of a query between two intermediate result sets for performance reasons.
SQL Fiddle (SQL Server)
Hi I know how to use the group by clause for sql. I am not sure how to explain this so Ill draw some charts. Here is my original data:
Name Location
----------------------
user1 1
user1 9
user1 3
user2 1
user2 10
user3 97
Here is the output I need
Name Location
----------------------
user1 1
9
3
user2 1
10
user3 97
Is this even possible?
The normal method for this is to handle it in the presentation layer, not the database layer.
Reasons:
The Name field is a property of that data row
If you leave the Name out, how do you know what Location goes with which name?
You are implicitly relying on the order of the data, which in SQL is a very bad practice (since there is no inherent ordering to the returned data)
Any solution will need to involve a cursor or a loop, which is not what SQL is optimized for - it likes working in SETS not on individual rows
Hope this helps
SELECT A.FINAL_NAME, A.LOCATION
FROM (SELECT DISTINCT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.NAME,
YT.LOCATION
FROM YOUR_TABLE_7 YT) A
As Jirka correctly pointed out, I was using the Outer select, distinct and raw Name unnecessarily. My mistake was that as I used DISTINCT , I got the resulted sorted like
1 1
2 user2 1
3 user3 97
4 user1 1
5 3
6 9
7 10
I wanted to avoid output like this.
Hence I added the raw id and outer select
However , removing the DISTINCT solves the problem.
Hence only this much is enough
SELECT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.LOCATION
FROM SO_BUFFER_TABLE_7 YT
Thanks Jirka
If you're using straight SQL*Plus to make your report (don't laugh, you can do some pretty cool stuff with it), you can do this with the BREAK command:
SQL> break on name
SQL> WITH q AS (
SELECT 'user1' NAME, 1 LOCATION FROM dual
UNION ALL
SELECT 'user1', 9 FROM dual
UNION ALL
SELECT 'user1', 3 FROM dual
UNION ALL
SELECT 'user2', 1 FROM dual
UNION ALL
SELECT 'user2', 10 FROM dual
UNION ALL
SELECT 'user3', 97 FROM dual
)
SELECT NAME,LOCATION
FROM q
ORDER BY name;
NAME LOCATION
----- ----------
user1 1
9
3
user2 1
10
user3 97
6 rows selected.
SQL>
I cannot but agree with the other commenters that this kind of problem does not look like it should ever be solved using SQL, but let us face it anyway.
SELECT
CASE main.name WHERE preceding_id IS NULL THEN main.name ELSE null END,
main.location
FROM mytable main LEFT JOIN mytable preceding
ON main.name = preceding.name AND MIN(preceding.id) < main.id
GROUP BY main.id, main.name, main.location, preceding.name
ORDER BY main.id
The GROUP BY clause is not responsible for the grouping job, at least not directly. In the first approximation, an outer join to the same table (LEFT JOIN below) can be used to determine on which row a particular value occurs for the first time. This is what we are after. This assumes that there are some unique id values that make it possible to arbitrarily order all the records. (The ORDER BY clause does NOT do this; it orders the output, not the input of the whole computation, but it is still necessary to make sure that the output is presented correctly, because the remaining SQL does not imply any particular order of processing.)
As you can see, there is still a GROUP BY clause in the SQL, but with a perhaps unexpected purpose. Its job is to "undo" a side effect of the LEFT JOIN, which is duplication of all main records that have many "preceding" ( = successfully joined) records.
This is quite normal with GROUP BY. The typical effect of a GROUP BY clause is a reduction of the number of records; and impossibility to query or test columns NOT listed in the GROUP BY clause, except through aggregate functions like COUNT, MIN, MAX, or SUM. This is because these columns really represent "groups of values" due to the GROUP BY, not just specific values.
If you are using SQL*Plus, use the BREAK function. In this case, break on NAME.
If you are using another reporting tool, you may be able to compare the "name" field to the previous record and suppress printing when they are equal.
If you use GROUP BY, output rows are sorted according to the GROUP BY columns as if you had an ORDER BY for the same columns. To avoid the overhead of sorting that GROUP BY produces, add ORDER BY NULL:
SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY NULL;
Relying on implicit GROUP BY sorting in MySQL 5.6 is deprecated. To achieve a specific sort order of grouped results, it is preferable to use an explicit ORDER BY clause. GROUP BY sorting is a MySQL extension that may change in a future release; for example, to make it possible for the optimizer to order groupings in whatever manner it deems most efficient and to avoid the sorting overhead.
For full information - http://academy.comingweek.com/sql-groupby-clause/
SQL GROUP BY STATEMENT
SQL GROUP BY clause is used in collaboration with the SELECT statement to arrange identical data into groups.
Syntax:
1. SELECT column_nm, aggregate_function(column_nm) FROM table_nm WHERE column_nm operator value GROUP BY column_nm;
Example :
To understand the GROUP BY clauserefer the sample database.Below table showing fields from “order” table:
1. |EMPORD_ID|employee1ID|customerID|shippers_ID|
Below table showing fields from “shipper” table:
1. | shippers_ID| shippers_Name |
Below table showing fields from “table_emp1” table:
1. | employee1ID| first1_nm | last1_nm |
Example :
To find the number of orders sent by each shipper.
1. SELECT shipper.shippers_Name, COUNT (orders.EMPORD_ID) AS No_of_orders FROM orders LEFT JOIN shipper ON orders.shippers_ID = shipper.shippers_ID GROUP BY shippers_Name;
1. | shippers_Name | No_of_orders |
Example :
To use GROUP BY statement on more than one column.
1. SELECT shipper.shippers_Name, table_emp1.last1_nm, COUNT (orders.EMPORD_ID) AS No_of_orders FROM ((orders INNER JOIN shipper ON orders.shippers_ID=shipper.shippers_ID) INNER JOIN table_emp1 ON orders.employee1ID = table_emp1.employee1ID)
2. GROUP BY shippers_Name,last1_nm;
| shippers_Name | last1_nm |No_of_orders |
for more clarification refer my link
http://academy.comingweek.com/sql-groupby-clause/
I use this query to
SELECT userId, submDate, COUNT(submId) AS nSubms
FROM submissions
GROUP BY userId, submDate
ORDER BY userId, submDate
obtain the total number of submissions per user per date.
However I need to have the progressive count for every user so I can see how their submissions accumulate over time.
Is this possible to implement in a query ?
EDIT: The obtained table looks like this :
userId submDate nSubms
1 2-Feb 1
1 4-Feb 7
2 1-Jan 4
2 2-Jan 2
2 18-Jan 1
I want to produce this :
userId submDate nSubms progressive
1 2-Feb 1 1
1 4-Feb 7 8
2 1-Jan 4 4
2 2-Jan 2 6
2 18-Jan 1 7
EDIT 2 : Sorry for not mentioning it earlier, I am not allowed to use :
Stored procedure calls
Update/Delete/Insert/Create queries
Unions
DISTINCT keyword
as I am using a tool that doesn't allow those.
You can use a self-join to grab all the rows of the same table with a date before the current row:
SELECT s0.userId, s0.submDate, COUNT(s0.submId) AS nSubms, COUNT (s1.submId) AS progressive
FROM submissions AS s0
JOIN submissions AS s1 ON s1.userId=s0.userId AND s1.submDate<=s0.submDate
GROUP BY s0.userId, s0.submDate
ORDER BY s0.userId, s0.submDate
This is going to force the database to do a load of pointless work counting all the same rows again and again though. It would be better to just add up the nSubms as you go down in whatever script is calling the query, or in an SQL variable, if that's available in your environment.
The Best solution for this is to do it at the client.
It's the right tool for the job. Databases are not suited for this kind of task
Select S.userId, S.submDate, Count(*) As nSubms
, (Select Count(*)
From submissions As S1
Where S1.userid = S.userId
And S1.submDate <= S.submDate) As TotalSubms
From submissions As S
Group By S.userid, S.submDate
Order By S.userid, S.submDate