How to split quantities in separate columns and lines - sqlite

Details
These are my tables and data:
create table orders (
id int not null,
item varchar(10),
quantity int
);
insert into orders (id, item, quantity) values
(1, 'Item 1', 10);
create table orders_picked (
id int not null,
orderId int,
quantity int
);
insert into orders_picked (id, orderId, quantity) values
(1, 1, 4),
(2, 1, 1);
To get a count of picked Items, I run this query:
select item, sum(op.quantity) as quantity from orders o left join orders_picked op on o.id = op.orderId group by item
And this is the output:
Question
Because the table orders has 5 remaining items to be picked. How can I display the remaining items in separate lines, with an extra column to identify whether it's "picked" or "remaining"? Like so:

Use a CTE to join the tables and to aggregate and then use UNION ALL to get separate rows for picked and remaining:
WITH cte AS (
SELECT o.item,
o.quantity,
TOTAL(op.quantity) AS picked_quantity
FROM orders o LEFT JOIN orders_picked op
ON op.orderId = o.id
)
SELECT item, picked_quantity AS quantity, 'Picked' AS Type
FROM cte
UNION ALL
SELECT item, quantity - picked_quantity, 'Remaining'
FROM cte;
See the demo.

Related

Pull data from two separate SQL tables based on a JOIN but that uses a column not in the JOIN or the SELECT

This is based on a Khan Academy course. I have 2 SQLite tables:
CREATE TABLE table1 (id STRING PRIMARY KEY, charge_id TEXT, amount INTEGER, currency INTEGER, country STRING);
INSERT INTO table1
( id, charge_id, amount, currency, country) VALUES
('0xb01', '0x1', 2000, 'USD', 'USA'),
('0x0a1', '0x1', 500, 'USD', 'USA'),
('0x0c1', '0x1', 1000, 'CAD', 'USA'),
('0xs31', '0x4', 1000, 'YEN', 'CA');
CREATE TABLE table2 (id STRING PRIMARY KEY, charge_id TEXT, value VARIABLE);
INSERT INTO table2
( id, charge_id, value ) VALUES
('0x34s', '0x1', '123 main street'),
('0x3ze', '0x1', 'merchant-id-001'),
('0x3w2', '0x2', 'zip-code-90210' ),
('0x35k', '0x2', 'merchant-id-002');
I would SELECT the amount, currency and country from table 1 (Charges) and join with table 2 (Metadata) based on the id. Charges uses ID, while Metadata stores meta tags, with a unique identifier [id] equal to the charge [id] from Charges. I want to group the total amount, total currency for each merchant_id and only those charges that were made in the USA.
Step-by-step pseudo code:
(1) find all charges in the USA (Charges country)
(2) match all charge_ids from Charges (id) to charges in Metadata (id)
(3) separate each charge by the merchant_id (Metadata value)
(4) display the total amount, currency by merchant_id (amount, Charges currency, value)
This is a difficult because :
(1) I want to select from Charges and
(2) join to Metadata by the [id]
(3) but each Metadata record only has the charge_id and a metadata tag, which would match the merchant_id with the charge
The query result I would like is:
value (merchant id) currency total amount
merchant-id-001 usd 2500
merchant-id-001 cad 1000
merchant-id-002 yen 200
merchant-id-002 cad 50
Currently I have this query but it does not seem to be working:
select table1.amount, table1.currency, table1.country, count(*)
from table1
LEFT JOIN table1
UNION ALL
SELECT table2.value
FROM CHARGES_table2
LEFT JOIN table2
ON table1.id = table2.id
WHERE table1.country = 'USA'
GROUP BY table2.value
I am getting errors on union parameters: 2,1
Read the grammar & other documentation for the expressions you are using. The arguments to UNION are two SELECTs & it can have a final ORDER BY. Here's the parse:
select table1.amount, table1.currency, table1.country, count(*)
from table1
LEFT JOIN table1
UNION ALL
SELECT table2.value
FROM CHARGES_table2
LEFT JOIN table2
ON table1.id = table2.id
WHERE table1.country = 'USA'
GROUP BY table2.value
UNION is putting its arguments' rows into one table so it also requires that their columns agree in number & have compatible types. Here the numbers disagree.
There is no table1 in scope in the second SELECT so that is an error in isolation that is moot given the UNION.

Store child data in single column

I have the following tables:
CREATE TABLE titles (
id INTEGER,
title VARCHAR(255)
);
INSERT INTO titles (id, title) VALUES (1, "Mars Attacks!");
INSERT INTO titles (id, title) VALUES (2, "Da Vinci Code");
CREATE TABLE GenreName (
id INTEGER,
name VARCHAR(255)
);
INSERT INTO GenreName (id, name) VALUES (1, "Action");
INSERT INTO GenreName (id, name) VALUES (2, "Adventure");
INSERT INTO GenreName (id, name) VALUES (3, "Comedy");
INSERT INTO GenreName (id, name) VALUES (4, "Science-Fiction");
INSERT INTO GenreName (id, name) VALUES (5, "Thriller");
CREATE TABLE Genre (
title INTEGER,
genre INTEGER
);
INSERT INTO Genre (title, genre) VALUES (1, 1);
INSERT INTO Genre (title, genre) VALUES (1, 3);
INSERT INTO Genre (title, genre) VALUES (1, 4);
INSERT INTO Genre (title, genre) VALUES (2, 1);
INSERT INTO Genre (title, genre) VALUES (2, 5);
I'm looking for a way to retrieve the data as
Id Title Genre
1 Mars Attacks! Action, Comedy, Science-Fiction
2 Da Vinci Code Action, Thriller
I'm stuck with the recursive way of selecting the data.
I have a DBFiddle
select t.id, t.title, group_concat(gn.name) from genere g join titles t on t.id = g.title join genrename gn on gn.id = g.genre group by t.id, t.title
Use SQL OUTER JOINS
select titles.id, name, title from titles left outer join genre on titles.id = genre.title left outer join genrename on genre.genre = genrename.id;

SQLite cross reference unique combinations

I've got two tables already populated with data with the given schemas:
CREATE TABLE objects
(
id BIGINT NOT NULL,
latitude BIGINT NOT NULL,
longitude BIGINT NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE tags
(
id BIGINT NOT NULL,
tag_key VARCHAR(100) NOT NULL,
tag_value VARCHAR(500),
PRIMARY KEY (id , tag_key)
)
object.id and tags.id refer to the same object
I'd like to populate a third table with the unique combinations of tag_key and tag_value. For example:
INSERT OR REPLACE INTO objects (id) VALUES (0);
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (0, 'a', 'x');
INSERT OR REPLACE INTO objects (id) VALUES (1);
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (1, 'a', 'y');
INSERT OR REPLACE INTO objects (id) VALUES (2);
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (2, 'a', 'x');
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (2, 'a', 'y');
INSERT OR REPLACE INTO objects (id) VALUES (3);
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (3, 'a', 'x');
INSERT OR REPLACE INTO objects (id) VALUES (4);
INSERT OR REPLACE INTO tags (id, tag_key, tag_value) VALUES (4, 'a', 'y');
Should result in 3 entries of
0: ([a,x])
1: ([a,y])
3: ([a,x][a,y])
Currently I have:
CREATE TABLE tags_combinations
(
id INTEGER PRIMARY KEY,
tag_key VARCHAR(100) NOT NULL,
tag_value VARCHAR(500)
);
The id shouldn't be related to the original id of the object, just something to group unique combinations.
This is the query I have so far:
SELECT
t1.tag_key, t1.tag_value
FROM
tags t1
WHERE
t1.id
IN
(
/* select ids who's every tags entry is not under one id in tags_combinations */
SELECT
t2.id
FROM
tags t2
WHERE
t2.tag_key, t2.tag_value
NOT IN
(
)
);
The part with the comment is what I am not sure about, how would I select every id from tags that does not have all of the corresponding tag_key and tag_value entries already under one id in tags_combinations?
To clarify exactly the result I am after: From the sample data given, it should return 4 rows with:
row id tag_key tag_value
0 0 a x
1 1 a y
2 2 a x
3 2 a y
SQL is a set-based language. If you reformulate your question in the language of set theory, you can directly translate it into SQL:
You want all rows of the tags table, except those from duplicate objects.
Objects are duplicates if they have exactly the same key/value combinations. However, we still want to return one of those objects, so we define duplicates only as those objects where no other duplicate object with a smaller ID exists.
Two objects A and B have exactly the same key/value combinations if
all key/value combinations in A also exist in B, and
all key/value combinations in B also exist in A.
All key/value combinations in A also exist in B if there is no key/value combination in A that does not exist in B (note: double negation).
SELECT id, tag_key, tag_value
FROM tags
WHERE NOT EXISTS (SELECT 1
FROM tags AS dup
WHERE dup.id < tags.id
AND NOT EXISTS (SELECT 1
FROM tags AS A
WHERE A.id = tags.id
AND NOT EXISTS (SELECT 1
FROM tags AS B
WHERE B.id = dup.id
AND B.tag_key = A.tag_key
AND B.tag_value = A.tag_value)
)
AND NOT EXISTS (SELECT 1
FROM tags AS B
WHERE B.id = dup.id
AND NOT EXISTS (SELECT 1
FROM tags AS A
WHERE A.id = tags.id
AND A.tag_key = B.tag_key
AND A.tag_value = B.tag_value)
)
)
ORDER BY id, tag_key;
This is not easy in SQLite. We want to identify groups of tag key/value pairs. So we could group by id and get a string of the associated pairs with group_concat. This would be the way to do it in another DBMS. SQLite, however, cannot order in group_concat, so we might end up with 2: 'a/x,a/y' and 5: 'a/y,a/x'. Two different strings for the same pairs.
Your best bet may be to write a program and find the distinct pairs iteratively.
In SQLite you may want to try this:
insert into tags_combinations (id, tag_key, tag_value)
select id, tag_key, tag_value
from tags
where id in
(
select min(id)
from
(
select id, group_concat(tag_key || '/' || tag_value) as tag_pairs
from
(
select id, tag_key, tag_value
from tags
order by id, tag_key, tag_value
) ordered_data
group by id
) aggregated_data
group by tag_pairs
);
Ordering the data before applying group_concat is likely to get the tag pairs ordered, but in no way guaranteed! If this is something you want to do only once, it may be worth a try, though.
To merge multiple rows into one value, you need a function like group_concat().
The ORDER BY is needed to ensure a consistent order of the rows within a group:
SELECT DISTINCT group_concat(tag_key) AS tag_keys,
group_concat(tag_value) AS tag_values
FROM (SELECT id,
tag_key,
tag_value
FROM tags
ORDER BY id,
tag_key,
tag_value)
GROUP BY id;
If you want to have keys and values interleaved, as shown in the question, you need to do more string concatenation:
SELECT DISTINCT group_concat(tag_key || ',' || tag_value, ';') AS keys_and_values
FROM (...

SQLite FTS4 with preferred language

I have an SQLite table that was generated by using the FTS4 module. Each entry is listed at least twice with different languages, but still sharing a unique ID (int column, not indexed).
Here is what I want to do:
I want to lookup a term in a preferred language. I want to union the result with a lookup for the same term using another language.
For the second lookup though, I want to ignore all entries (identified by their ID) that I already found during the first lookup. So basically I want to do this:
WITH term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1)
SELECT *
FROM term_search1
UNION
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 2
AND id NOT IN (SELECT id FROM term_search1)
The problem here is, that the term_seach1 Query would be executed twice. Is there a way of materializing my results maybe? Any solution for limiting it to 2 Queries (instead of 3) would be great.
I also tried using recursive Queries, something like:
WITH RECURSIVE term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1
UNION ALL
SELECT m.*
FROM myFts m LEFT OUTER JOIN term_search1 t ON (m.id = t.id)
WHERE myFts MATCH 'term'
AND m.languageId = 2
AND t.id IS NULL
)
SELECT * FROM term_search1
This didn't work neither. Apparently he just executed two lookups for languageId = 2 (is this a bug maybe?).
Thanks in advance :)
You can use TEMPORARY tables to reduce the number of queries to myFts to 2:
CREATE TEMP TABLE results (id INTEGER PRIMARY KEY);
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 1;
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 2
AND id NOT IN (SELECT id FROM results);
SELECT * FROM myFts
WHERE id IN (SELECT id FROM results);
DROP TABLE results;
If it's possible to change the schema, you should only keep text data in the FTS table. This way you will avoid incorrect results when you are searching for numbers and rows matching languageId is not desired. Create another meta table holding non-textual data (like id and languageId) and filter the rows by joining against the rowid of the myFts. This way you will need to query the FTS table only once - use the temporary table to store the FTS table results then use the meta table to order them.
This is the best I can think of :
SELECT *
FROM myFts t1
JOIN (SELECT COUNT(*) AS cnt, id
FROM myFts t2
WHERE t2.languageId in (1, 2)
AND t2.myFts MATCH 'term'
GROUP BY t2.id) t3
ON t1.id = t3.id
WHERE t1.myFts MATCH 'term'
AND t1.languageId in (1, 2)
AND (t1.languageId = 1 or t3.cnt = 1)
I am not sure if the second MATCH clause is necessary.
The idea is to first count the acceptable rows, then choose the best one.
Edit : I have no idea why it does not work with your table. This is what I did to test it (SQLite version 3.8.10.2):
CREATE VIRTUAL TABLE myFts USING fts4(
id integer,
languageId integer,
content TEXT
);
insert into myFts(id, languageId, content) values (10, 1, 'term 10 lang 1');
insert into myFts(id, languageId, content) values (10, 2, 'term 10 lang 2');
insert into myFts(id, languageId, content) values (11, 1, 'term 11 lang 1');
insert into myFts(id, languageId, content) values (12, 2, 'term 12 lang 2');
insert into myFts(id, languageId, content) values (13, 1, 'not_erm 13 lang 1');
insert into myFts(id, languageId, content) values (13, 2, 'term 13 lang 2');
executing the query gives :
sqlite> SELECT *
...> FROM myFts t1
...> JOIN (SELECT COUNT(*) AS cnt, id
...> FROM myFts t2
...> WHERE t2.languageId in (1, 2)
...> AND t2.myFts MATCH 'term'
...> GROUP BY t2.id) t3
...> ON t1.id = t3.id
...> WHERE t1.myFts MATCH 'term'
...> AND t1.languageId in (1, 2)
...> AND (t1.languageId = 1 or t3.cnt = 1);
10|1|term 10 lang 1|2|10
11|1|term 11 lang 1|1|11
12|2|term 12 lang 2|1|12
13|2|term 13 lang 2|1|13
sqlite>

How To find Category Level ON POS Database

I am trying to find Retail Category node level at POS database, in AOS table ECORESCATEGORY has field LEVEL_ with correct values, but on POS, this field is just showing zero value for all items.
How can i make sure that this field gets synced with AOS?
I tried jobs N-1040 and N-1100, but this field is not updated.
Following is SQL query i use:
select distinct Category, it.ItemID, MAX(IName.Name) Name, isnull(Level_,0) -1
from InventTable it Inner Join
(
Select Distinct PRODUCT, category, CATEGORYHIERARCHY
From EcoResProductCategory
Where CATEGORYHIERARCHY In (select Top 1 CATEGORYHIERARCHY
from EcoResCategoryHIERARCHYRole
where NAMEDCATEGORYHIERARCHYROLE = 4 --Retail
)
) pcat on it.PRODUCT = pcat.PRODUCT Left Join
EcoResCategory cat on pcat.category = cat.recid Left Join
EcoResProductTranslation IName on it.PRODUCT = iname.PRODUCT
group by Category, it.ItemID, isnull(Level_,0) -1

Resources