Define a relationship as a union - database

In several parts of my code I have for example these lines:
$user->movements()->get();
...
$user->movements()->where(...)->get();
...
$user->movements()->where(...)->select(... sum, count, avg ...)->get();
...
But now I'm facing an important change on the movements table structure.
Now I need two tables with a similar structure (the data HAVE to be in separate tables), movements and ticket_movements.
These tables are consulted by two system, one of then needs the data to be separated and the other needs the data to be as in one table.
So, in one of the systems, I would like to define the relationship movements() as an union of movements and ticket_movements tables.
So, having the relationship movements defined as:
public function movements()
{
$movements = $this->hasMany('App\Model\Movement')
->select(\DB::raw("
id,
user_id,
movement_type_id,
amount,
description
"));
$tickets_movements = $this->hasMany('App\Model\TicketMovement')
->select(\DB::raw("
id,
user_id,
movement_type_id,
amount,
description
"));
return $movements->union($tickets_movements->getQuery());
}
If I do this:
$user->movements()
->whereIn('movement_type_id', [1, 2])
->select(\DB::raw('
SUM(CASE WHEN movement_type_id = 1 THEN 1 ELSE 0 END) as credit,
SUM(CASE WHEN movement_type_id = 2 THEN 1 ELSE 0 END) as debit
'))
->first();
The query I get is:
SELECT
SUM(CASE WHEN movement_type_id = 1 THEN 1 ELSE 0 END) as credit,
SUM(CASE WHEN movement_type_id = 2 THEN 1 ELSE 0 END) as debit
FROM "movements"
WHERE "movements"."user_id" = 2
AND "movements"."user_id" is not null
AND "movement_type_id" in (1, 2)
UNION
SELECT id, user_id, movement_type_id, amount, description
FROM "ticket_movements"
WHERE "ticket_movements"."user_id" = 2
AND "ticket_movements"."user_id" is not null limit 1
Besides it's not the query I need, it give me an error because of the columns:
Syntax error: 7 ERROR:
each UNION query must have the same number of columns
The query I need is something like this:
SELECT
SUM(CASE WHEN movement_type_id = 1 THEN 1 ELSE 0 END) as credit,
SUM(CASE WHEN movement_type_id = 2 THEN 1 ELSE 0 END) as debit
FROM (
SELECT id, user_id, movement_type_id, amount, description
FROM "movements"
UNION
SELECT id, user_id, movement_type_id, amount, description
FROM "ticket_movements" ) as movements
WHERE "movements"."user_id" = 2
AND "movements"."movement_type_id" in (1, 2)
Without modifying each line where I do $user->movements()...
I don't know is that is possible...

Related

SQL Server : how to get DISTINCT COUNT for every GROUP

I have a table like this:
I would like to get the distinct counts for every GROUP:
How to get the result? Thank you.
You can use conditional aggregation:
select group,
sum(case when location = 'A' then 1 else 0 end) as a,
sum(case when location = 'B' then 1 else 0 end) as b,
sum(case when location = 'C' then 1 else 0 end) as c
from t
group by group;
Note that group is a very poor name for a column because it is a SQL reserved word.

Query returns two lines

I have a table that has a column called type which has either a value of invoiced or order and then another column holding the value along with a column holding the customer number etc.
I have written a script :-
select
customer,
(CASE WHEN TYPE = 'INVOICED' THEN SUM(INVTOTAL) else 0 END) AS INVTOTAL,
(CASE WHEN TYPE = 'ORDERS' THEN SUM(INVTOTAL) else 0 END) AS ORDERTOTAL
from
salestable
Why does it return the following?
customer INVTOTAL ORDERTOTAL
Joe Bloggs 1000 0
Joe Bloggs 0 1300
instead of
customer INVTOTAL ORDERTOTAL
Joe Bloggs 1000 1300
Sorry to ask such a novice question but I am new to SQL and learning it...
Thanks for any help!
Your query was missing a group by. Also use sum around case to avoid multiple rows.
select customer,
sum(CASE WHEN TYPE = 'INVOICED' THEN INVTOTAL else 0 END) AS INVTOTAL,
sum(CASE WHEN TYPE = 'ORDERS' THEN INVTOTAL else 0 END) AS ORDERTOTAL
from salestable
group by customer
You need to do a group by with the customer that will avoid the multiple rows.check the fiddle below.
create table tb1
(customer varchar(25),
type varchar(25),
invoice numeric(18,2)
);
insert into tb1(customer,type,invoice) values('Joe Bloggs','INVOICED',1000);
insert into tb1(customer,type,invoice) values('Joe Bloggs','ORDERS',1000);
select customer,
sum(CASE WHEN TYPE = 'INVOICED' THEN sum(invoice) else 0 END) AS INVTOTAL,
sum(CASE WHEN TYPE = 'ORDERS' THEN sum(invoice) else 0 END) AS ORDERTOTAL
from tb1
group by customer
fiddle with example
You need to put the entire case statment inside sum()
inorder to avoid grouping by type also else you will get this below error
Column 'tb1.type' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause.
Because select returns a row for each row in the table matching the where filters. If no filters, then a result row for every row in table. If you want to "join" the rows, you could try grouping by the customer, like:
SELECT customer,
SUM(CASE WHEN TYPE = 'INVOICED' THEN INVTOTAL ELSE 0 END) as INVTOTAL,
SUM(CASE WHEN TYPE = 'ORDERS' THEN INVTOTAL ELSE 0 END) as ORDERTOTAL
FROM salestable
GROUP BY customer

Passing values into CASE statement

and thank you all in advance for your help.
I'm trying to take the results from two separate queries and include them in a third query that has a CASE statement. I've had some success but I'm not able to present the results of the third query in the proper order. The purpose of this is to show the employee count for each department under the different managers. So far I can only load separately the manager names and their departments and employee department count totals by department. What I can't figure out is how to get the manager names in and the employee department count in for each manager row. Below are the two source queries I've used so far and the query with the CASE statement. I've also looked at UNPIVOT function with no success yet.
a) This simple query lists each primary manager name. There are also sub managers that will be returned using a hierarchy query later.
select name from employees "Boss" where employeeid in
(‘1’,'5','25','84','85');
b) This query returns the department id count for each main manager (‘1’,'5','25','84','85') as well as all sub-managers.
select departmentid, count(departmentid) COUNT from employees
where departmentid = departmentid and level <= 3
connect by prior employeeid = bossid
start with employeeid = 5
group by departmentid
order by departmentid;
c) Here’s a CASE statement that outputs exactly as desired. The problem here is the select statement currently outputs only the manager names and the manager departments into the columns. What I need to do is output both the manager names and the manager's employee department counts into the individual manager row columns. I've tried to do a separate select of the manager names to get the ‘Boss’ column and another select to include the department counts. But that got messy. Also passing the counts in a second statement would create an additional unwanted column.
select e.name "Boss",
COUNT(CASE WHEN d.departmentid = '1' THEN 1 END) AS "Finance",
COUNT(CASE WHEN d.departmentid = '2' THEN 1 END) AS "HR",
COUNT(CASE WHEN d.departmentid = '3' THEN 1 END) AS "IT",
COUNT(CASE WHEN d.departmentid = '4' THEN 1 END) AS "Marketing",
COUNT(CASE WHEN d.departmentid = '5' THEN 1 END) AS "Sales"
from employees e, departments d
where e.employeeid in (select distinct e.bossid from employees e)
and e.departmentid = d.departmentid (+)
group by e.name
order by e.name;
Boss Finance HR IT Marketing Sales
-------------------- ---------- ---------- ---------- ---------- ----------
Baxter Carney 0 0 0 0 1
Blythe Pierce 0 0 0 0 1
Here's an altered CASE query that loads the employee department counts but unfortunately it loads by department and not by individual manager. That is the problem I'm stuck on right now. How to pass the counts to the right manager and into the right column.
select departmentid "DEPTNO",
COUNT(CASE WHEN departmentid = '1' THEN 1 END) AS "Finance",
COUNT(CASE WHEN departmentid = '2' THEN 1 END) AS "HR",
COUNT(CASE WHEN departmentid = '3' THEN 1 END) AS "IT",
COUNT(CASE WHEN departmentid = '4' THEN 1 END) AS "Marketing",
COUNT(CASE WHEN departmentid = '5' THEN 1 END) AS "Sales"
from employees
where departmentid = departmentid and level <= 3
connect by prior employeeid = bossid
start with employeeid = 5
group by departmentid
order by departmentid
/
DEPTNO Finance HR IT Marketing Sales
3 0 0 1 0 0
5 0 0 0 0 21
And here's for all managers. You can see that it just keeps increasing the individual department count.
DEPTNO Finance HR IT Marketing Sales
1 4 0 0 0 0
2 0 23 0 0 0
3 0 0 20 0 0
4 0 0 0 1 0
5 0 0 0 0 28

Distinct counts per year

hello I am trying to count a person only once even if a person has two enrollments in the same year:
select DISTINCT Year, School,
count (case when [Graduate]= 1 AND [Dropout]= 0 THEN ID END) As 'passed',
count (case when [Graduate]= 0 AND [Dropout]= 1 THEN ID END) as 'dropped',
count (case when [Graduate]= 0 AND [Dropout]= 0 THEN ID END) as 'Continued'
from Table where ID = '10'
group by Year, School
my output is
Year school passed dropped continued
2012 School 0 0 1
2013 School 0 0 1
2014 School 0 0 2
continued is 2 for ID=1 because student enrolled twice. How do i get a distinct count in this case?
You are using DISTINCT wrong, it should be inside each COUNT:
SELECT [Year],
School,
COUNT(DISTINCT CASE WHEN [Graduate] = 1 AND [Dropout] = 0 THEN ID END) Passed,
COUNT(DISTINCT CASE WHEN [Graduate] = 0 AND [Dropout] = 1 THEN ID END) Dropped,
COUNT(DISTINCT CASE WHEN [Graduate] = 0 AND [Dropout] = 0 THEN ID END) Continued
FROM YourTable
WHERE ID = '10'
GROUP BY [Year],
School;

SQL Server Query that returns the number of Likes and number of Dislikes

I have a SQL server table that displays what a user Liked or Disliked in an item description. He/she can only like/dislike one item so it will display a 1 if Liked or 0 if Disliked on the Type Field.
What I want is for an output that counts the Likes and Dislikes that displays both of them as seen below. I tried to do inner joins and unions and can't get it to work. Any ideas?
SELECT ItemID,
Description,
SUM(CASE WHEN Type = 1 THEN 1 ELSE 0 END) AS Like,
SUM(CASE WHEN Type = 0 THEN 1 ELSE 0 END) AS Dislike
FROM Table
GROUP BY ItemID,
Description
try:
select ItemId,Description, sum(case when Type=1 then 1 else 0 end) 'like', // quote the word like
sum(case when Type = 0 then 1 else 0 end) 'dislike'
from tblFeedback
group by ItemId

Resources