SQLite Sum of Sums - sqlite

Let's say I have two tables which look like this:
Games:
| AwayTeam | HomeTeam | AwayPoints | HomePoints |
------------------------------------------------------
| Aardvarks | Bobcats | 2 | 1 |
| Bobcats | Caterpillars | 20 | 10 |
| Aardvarks | Caterpillars | 200 | 100 |
Teams:
| Name |
----------------
| Aardvarks |
| Bobcats |
| Caterpillars |
How can I make a result which looks like this?
| Name | TotalPoints |
------------------------------
| Aardvarks | 202 |
| Bobcats | 21 |
| Caterpillars | 110 |
I think my real problem is how to splice statements together in SQL. These two statements work well individually:
SELECT SUM ( AwayPoints )
FROM Games
WHERE AwayTeam='Bobcats';
SELECT SUM ( HomePoints )
FROM Games
WHERE HomeTeam='Bobcats';
I suspect that I need a compound operator if I want to splice two SELECT statements togeather. Then pass that statement into the aggregate expression below:
SELECT Name, SUM( aggregate_expression )
AS 'TotalPoints'
FROM Teams
GROUP BY Name;
If I had to just throw it all together, I think I'd end up with something like this:
SELECT Name, SUM (
SELECT SUM ( AwayPoints )
FROM Games
WHERE AwayTeam=Name
UNION
SELECT SUM ( HomePoints )
FROM Games
WHERE HomeTeam=Name
)
AS 'TotalPoints'
FROM Teams
GROUP BY Name;
However that doesn't work because SELECT SUM ( SELECT ... is completely invalid

Use a UNION ALL
SELECT team, SUM(points)
FROM (
SELECT HomeTeam AS team, SUM(HomePoints) AS points
FROM Games
GROUP BY HomeTeam
UNION ALL
SELECT AwayTeam AS team, SUM(AwayPoints) AS points
FROM Games
GROUP BY AwayTeam
)
GROUP BY team
See SQLite documentation for
SELECT expr AS alias
SELECT ... FROM ( select-stmt ) AS table-alias

Related

How can I set multiple aliases for a single derived table in MariaDB 5.5?

Consider a database with three tables:
goods (Id is the primary key)
+----+-------+-----+
| Id | Name | SKU |
+----+-------+-----+
| 1 | Nails | 123 |
| 2 | Nuts | 456 |
| 3 | Bolts | 789 |
+----+-------+-----+
invoiceheader (Id is the primary key)
+----+--------------+-----------+---------+
| Id | Date | Warehouse | BuyerId |
+----+--------------+-----------+---------+
| 1 | '2021-10-15' | 1 | 223 |
| 2 | '2021-09-18' | 1 | 356 |
| 3 | '2021-07-13' | 2 | 1 |
+----+--------------+-----------+---------+
invoiceitems (Id is the primary key)
+----+----------+--------+-----+-------+
| Id | HeaderId | GoodId | Qty | Price |
+----+----------+--------+-----+-------+
| 1 | 1 | 1 | 15 | 1.1 |
| 2 | 1 | 3 | 7 | 1.5 |
| 3 | 2 | 1 | 12 | 1.5 |
| 4 | 3 | 3 | 3 | 1.3 |
+----+----------+--------+-----+-------+
What I'm trying to do is to get the MAX(invoiceheader.Date) for every invoiceitems.GoodId. Or, in everyday terms, to find out, preferably in a single query, when was the last time any of the goods were sold, from a specific warehouse.
To do that, I'm using a derived query, and the solution proposed here . In order to be able to do that, I think that I need to have a way of giving multiple (well, two) aliases for a derived table.
My query looks like this at the moment:
SELECT tmp.* /* placing the second alias here, before or after tmp.* doesn't work */
FROM ( /* placing the second alias, tmpClone, here also doesn't work */
SELECT
invoiceheader.Id,
invoiceheader.Date,
invoiceitems.HeaderId,
invoiceitems.Id,
invoiceitems.GoodId
FROM invoiceheader
LEFT JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
WHERE invoiceheader.Warehouse = 3
AND invoiceheader.Date > '0000-00-00 00:00:00'
AND invoiceheader.Date IS NOT NULL
AND invoiceheader.Date > ''
AND invoiceitems.GoodId > 0
ORDER BY
invoiceitems.GoodId ASC,
invoiceheader.Date DESC
) tmp, tmpClone /* this doesn't work with or without a comma */
INNER JOIN (
SELECT
invoiceheader.Id,
MAX(invoiceheader.Date) AS maxDate
FROM tmpClone
WHERE invoiceheader.Warehouse = 3
GROUP BY invoiceitems.GoodId
) headerGroup
ON tmp.Id = headerGroup.Id
AND tmp.Date = headerGroup.maxDate
AND tmp.HeaderId = headerGroup.Id
Is it possible to set multiple aliases for a single derived table? If it is, how should I do it?
I'm using 5.5.52-MariaDB.
you can use both (inner select) and left join to achieve this for example:
select t1.b,(select t2.b from table2 as t2 where t1.x=t2.x) as 'Y' from table as t1 Where t1.y=(select t3.y from table3 as t3 where t2.a=t3.a)
While this doesn't answer my original question, it does solve the problem from which the question arose, and I'll leave it here in case anyone ever comes across a similar issue.
The following query does what I'd intended to do - find the newest sale date for the goods from the specific warehouse.
SELECT
invoiceheader.Id,
invoiceheader.Date,
invoiceitems.HeaderId,
invoiceitems.Id,
invoiceitems.GoodId
FROM invoiceheader
INNER JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
INNER JOIN (
SELECT
MAX(invoiceheader.Date) AS maxDate,
invoiceitems.GoodId
FROM invoiceheader
INNER JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
WHERE invoiceheader.Warehouse = 3
AND invoiceheader.Date > '0000-00-00 00:00:00'
AND invoiceheader.Date IS NOT NULL
AND invoiceheader.Date > ''
GROUP BY invoiceitems.GoodId
) tmpDate
ON invoiceheader.Date = tmpDate.maxDate
AND invoiceitems.GoodId = tmpDate.GoodId
WHERE invoiceheader.Warehouse = 3
AND invoiceitems.GoodId > 0
ORDER BY
invoiceitems.GoodId ASC,
invoiceheader.Date DESC
The trick was to join by taking into consideration two things - MAX(invoiceheader.Date) and invoiceitems.GoodId - since one GoodId can only appear once inside a specific invoiceheader / invoiceitems JOINing (strict limit imposed on the part of the code which inserts into invoiceitems).
Whether this is the most optimal solution (ignoring the redundant conditions in the query), and whether it would scale well, remains to be seen - it has been tested on tables with ~5000 entries for invoiceheader, ~60000 entries for invoiceitems, and ~4000 entries for goods. Execution time was < 1 sec.

union multiple tables and group with aggregation (SQlite)

I have such kind of a data:
table1
id | part | price
1 | ox900 | 100
2 | ox980 | 200
and
table2
id | part | price
1 | ox560 | 560
2 | ox980 | 120
as result I want to get such schema:
id | part | priceTable1 | priceTable2 | minPrice
1 | ox900 | 100 | | 100
1 | ox980 | 200 | 120 | 120
1 | ox560 | | 560 | 560
to simplify it can be without minPrice column...
now I have such query:
SELECT *
FROM (select part, price from supportContacts
union all
select part, price from supportContacts2)
group by part
but it's not exactly what I want to achieve.
Is it possible somehow to do, what I've described above?
Also a fiddle: http://sqlfiddle.com/#!7/f7401/7/0
SQLite does not support full outer joins, so get a list of all parts first, and then look up their prices with left outer joins:
SELECT part,
table1.price AS priceTable1,
table2.price AS priceTable2,
min(ifnull(table1.price, 'inf'),
ifnull(table2.price, 'inf')) AS minPrice
FROM (SELECT part FROM table1
UNION
SELECT part FROM table2)
LEFT JOIN table1 USING (part)
LEFT JOIN table2 USING (part);
(fiddle)

Inserting Duplicate Records into a Temporary Table

I have a table ABC with duplicate records. I want to Insert only the duplicate records into another table ABC_DUPE in same schema using Bteq.
Any suggestions ?
Thanks,
Mukesh
You can use QUALIFY statement to identify and output duplicates:
Since you didn't share your table... Then consider the following ABC table:
+----+----+----+
| f1 | f2 | f3 |
+----+----+----+
| 1 | a | x |
| 1 | b | y |
| 2 | a | z |
| 2 | b | w |
| 2 | a | n |
+----+----+----+
Where a unique record is determined by using fields f1 and f2. In this example the record where f1=2 and f2='a' is a duplicate with f3 values z and n. To output these we use qualify:
SELECT *
FROM ABC
QUALIFY COUNT(*) OVER (PARTITION BY f1, f2) > 1;
QUALIFY uses Window functions to determine which records to include in the outputted record set. Here we use window function COUNT(*) partitioning by our unique composite key f1, f2. We keep only records where the Count(*) over that partition is greater than 1.
This will output:
+----+----+----+
| f1 | f2 | f3 |
+----+----+----+
| 2 | a | z |
| 2 | a | n |
+----+----+----+
You can use this in a CREATE TABLE statement like:
CREATE TABLE ABC_DUPE AS
(
SELECT *
FROM ABC
QUALIFY COUNT(*) OVER (PARTITION BY f1, f2) > 1
) PRIMARY INDEX (f1, f2);

sqlite3 recursive aggregation of data

This may be a kind of the Knapsack problem.
I need to traverse a data table, group it by a column, choosing ones with better time.
Then repeat the previous step until a limit given by column CAPACITY is not reached.
This is the demo scenario:
create table if not exists data( vid num, size num, epid num, sid num, capacity num, dt );
delete from data;
insert into data(vid,size,epid,sid,capacity,dt)
values
(0,20,1,1,50,1100), -- 2nd choice
(0,20,1,1,50,1000), -- 1st choice
(0,20,1,1,50,1200), -- last choice excluded because out of capacity
(1,20,2,2,50,1100), -- 2nd choice
(1,20,2,2,50,1000), -- 1st choice
(1,20,2,2,50,1200); -- last choice excluded because out of capacity
This is the non recursive solution:
with best0 as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
),
best1 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best0 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
),
best2 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best1 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0 union all select tid from best1)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best0
union all
select * from best1
union all
select * from best2
And this the result:
tid | vid | sid | size | Dtime | capacity | group_level
--- | --- | --- | ---- | ----- | -------- | -----------
2 | 0 | 1 | 20 | 1000 | 30 | 0
5 | 1 | 2 | 20 | 1000 | 30 | 0
1 | 0 | 1 | 20 | 1100 | 10 | 1
4 | 1 | 2 | 20 | 1100 | 10 | 1
This is the recursive version that give error: "recursive reference in a subquery: best"
with recursive best(tid,vid,sid,size,dt,remains,level)
as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
union all
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best) and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best
I tried differents solutions even using a loop counter but everyone give the same error.

Sqllite query on distinct or certain value

I have an android sqllite database. It has a text column called chainid.
I'd like to return all columns from rows with DISTINCT chainids || or where chainid is equal to: "none".
So e.g.:
| ID| Name | chainid |
| 1 | widgetname1 | 12345 |
| 2 | widgetname2 | 12345 |
| 3 | widgetname3 | "none" |
| 4 | widgetname4 | 49390 |
| 5 | widgetname5 | 49390 |
Given the above table I would like my query to return 3 rows with all columns for row 2, row3 and row5. -- So DISTINCT on chainid OR where chainid = "none" with the max id selected as the distinct row
Can I achieve this in one query?
I could return all and then process afterwards in java, but this is inefficient.
What about
select *
from table where id in
( select max(id)
from table
group by chainid
where chainid != 'none'
union
select id
from table
where chainid = 'none'
)

Resources